Version in base suite: 4.22.10+dfsg-0+deb13u1 Base version: samba_4.22.10+dfsg-0+deb13u1 Target version: samba_4.22.11+dfsg-0+deb13u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/s/samba/samba_4.22.10+dfsg-0+deb13u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/s/samba/samba_4.22.11+dfsg-0+deb13u1.dsc VERSION | 2 WHATSNEW.txt | 111 +++++++++ ctdb/common/ctdb_io.c | 20 + ctdb/common/pkt_read.c | 19 + ctdb/common/sock_daemon.c | 26 ++ ctdb/doc/ctdb.7.xml | 27 +- ctdb/protocol/protocol_basic.c | 15 + ctdb/protocol/protocol_types.c | 60 +++++ ctdb/server/ctdb_call.c | 38 +++ ctdb/server/ctdb_client.c | 9 ctdb/server/ctdb_control.c | 19 + ctdb/server/ctdb_ltdb_server.c | 7 ctdb/server/ctdb_persistent.c | 10 ctdb/server/ctdb_recover.c | 38 ++- ctdb/server/ctdb_traverse.c | 8 ctdb/server/ctdb_update_record.c | 10 debian/changelog | 41 +++ libcli/security/security_descriptor.c | 15 - librpc/idl/dns.idl | 6 librpc/ndr/ndr_dns.c | 11 source4/dns_server/dns_crypto.c | 44 ++- source4/dns_server/dns_query.c | 62 +++-- source4/dsdb/samdb/ldb_modules/audit_util.c | 180 ++++++++++++++++ source4/dsdb/samdb/ldb_modules/rootdse.c | 55 ++++ source4/dsdb/samdb/ldb_modules/wscript_build_server | 2 source4/kdc/kpasswd-service.c | 2 source4/ldap_server/ldap_backend.c | 27 +- source4/librpc/tests/dns-decode_dns_name_packet-hex.txt | 1 28 files changed, 780 insertions(+), 85 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp3of9ek5t/samba_4.22.10+dfsg-0+deb13u1.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmp3of9ek5t/samba_4.22.11+dfsg-0+deb13u1.dsc: no acceptable signature found diff -Nru samba-4.22.10+dfsg/VERSION samba-4.22.11+dfsg/VERSION --- samba-4.22.10+dfsg/VERSION 2026-05-15 13:51:43.443060200 +0000 +++ samba-4.22.11+dfsg/VERSION 2026-07-21 15:37:04.175275800 +0000 @@ -27,7 +27,7 @@ ######################################################## SAMBA_VERSION_MAJOR=4 SAMBA_VERSION_MINOR=22 -SAMBA_VERSION_RELEASE=10 +SAMBA_VERSION_RELEASE=11 ######################################################## # If a official release has a serious bug # diff -Nru samba-4.22.10+dfsg/WHATSNEW.txt samba-4.22.11+dfsg/WHATSNEW.txt --- samba-4.22.10+dfsg/WHATSNEW.txt 2026-05-15 13:51:43.444060000 +0000 +++ samba-4.22.11+dfsg/WHATSNEW.txt 2026-07-21 15:37:04.175275800 +0000 @@ -1,4 +1,112 @@ =============================== + Release Notes for Samba 4.22.11 + July 28, 2026 + =============================== + + +This is a security release in order to address the following defects: + +o CVE-2026-6949: TSIG packet with name compression can crash DNS + + Incorrect size calculations when a TSIG record contains + compressed names can lead to a large out-of-bounds write + causing the server to crash. + + https://www.samba.org/samba/security/CVE-2026-6949.html + + +o CVE-2026-58216: An authenticated user could possibly crash a KDC process + + A kpasswd packet that contains malformed ASN.1 might cause + the server to access 6 bytes of unallocated memory. This + memory is not exposed to the user, but in some + circumstances the server could crash. + + https://www.samba.org/samba/security/CVE-2026-58216.html + + +o CVE-2026-58218: DNS signing DoS via TKEY name cache exhaustion + + An unauthenticated user can repeatedly register names TKEY + names, which floods a cache causing legitimate TKEYs to be + expunged. This can practically block the use DNS TSIG + signing. + + https://www.samba.org/samba/security/CVE-2026-58218.html + + +o CVE-2026-58221: Samba AD authenticated LDAP access domain takeover + + Samba AD low-privilege authenticated LDAP access allows + modifications to internal LDB special DNs, which permits a + domain takeover. + + https://www.samba.org/samba/security/CVE-2026-58221.html + + +o CVE-2026-58222: Samba AD LDAP Compare filter injection and trusted-request + confusion disclose protected attributes + + An ordinary authenticated domain user can bypass access + checks and query confidential Active Directory attributes + (such as KDS root keys) via LDAP Compare requests. Due to a + filter injection flaw and trusted execution context, the + LDAP Compare operation can be turned into a + protected-attribute disclosure oracle. + + https://www.samba.org/samba/security/CVE-2026-58222.html + + +o CVE-2026-58224 The CTDB protocol has bounds checking issues + + CTDB fails to do integrity checking of received packets. + This includes failure to check field lengths against packet + lengths when unmarshalling packets. + + https://www.samba.org/samba/security/CVE-2026-58224.html + + +Changes since 4.22.10 +--------------------- + +o Douglas Bagnall + * BUG 16087: CVE-2026-58216 + +o Volker Lendecke + * BUG 16115: CVE-2026-58218 + +o Stefan Metzmacher + * BUG 16083: CVE-2026-6949 + * BUG 16147: CVE-2026-58221 + * BUG 16148: CVE-2026-58222 + +o Martin Schwenke + * BUG 16085: CVE-2026-58224 + +####################################### +Reporting bugs & Development Discussion +####################################### + +Please discuss this release on the samba-technical mailing list or by +joining the #samba-technical:matrix.org matrix room, or +#samba-technical IRC channel on irc.libera.chat. + +If you do report problems then please try to send high quality +feedback. If you don't provide vital information to help us track down +the problem then you will probably be ignored. All bug reports should +be filed under the Samba 4.1 and newer product in the project's Bugzilla +database (https://bugzilla.samba.org/). + + +====================================================================== +== Our Code, Our Bugs, Our Responsibility. +== The Samba Team +====================================================================== + + +Release notes for older releases follow: +---------------------------------------- + =============================== Release Notes for Samba 4.22.10 May 26, 2026 =============================== @@ -120,8 +228,7 @@ ====================================================================== -Release notes for older releases follow: ----------------------------------------- +---------------------------------------------------------------------- ============================== Release Notes for Samba 4.22.9 April 09, 2026 diff -Nru samba-4.22.10+dfsg/ctdb/common/ctdb_io.c samba-4.22.11+dfsg/ctdb/common/ctdb_io.c --- samba-4.22.10+dfsg/ctdb/common/ctdb_io.c 2025-01-30 13:57:43.587315800 +0000 +++ samba-4.22.11+dfsg/ctdb/common/ctdb_io.c 2026-07-21 15:37:04.175275800 +0000 @@ -115,7 +115,25 @@ return; } - /* Extract complete packet */ + /* + * Extract complete packet + * + * Yes, this allows an out-of-memory denial of service (DoS) + * attack if a number of packets are received with + * unreasonable packet sizes, noting that the maximum packet + * size is ~4GB. The CTDB protocol was not designed with a + * maximum packet size in mind, so CTDB may send very large + * valid packets. This means that imposing an arbitrary limit + * on incoming packets is not reasonable. Arguments that + * header fields, such as magic and/or version, should be + * validated before allocating a packet buffer are spurious + * from a security perspective because an attacker attempting + * DoS can send packets with valid headers. The private + * network and ctdbd socket should be secured against + * untrusted access, so reports that this possible DoS vector + * represents a security issue will be ignored. See the + * "Private addresses" section in ctdb(7) for more details. + */ data = talloc_memdup(queue->data_pool, queue->buffer.data + queue->buffer.offset, pkt_size); diff -Nru samba-4.22.10+dfsg/ctdb/common/pkt_read.c samba-4.22.11+dfsg/ctdb/common/pkt_read.c --- samba-4.22.10+dfsg/ctdb/common/pkt_read.c 2025-01-30 13:57:43.588315700 +0000 +++ samba-4.22.11+dfsg/ctdb/common/pkt_read.c 2026-07-21 15:37:04.175275800 +0000 @@ -146,6 +146,25 @@ return; } + /* + * Allocate buffer for entire packet + * + * Yes, this allows an out-of-memory denial of service (DoS) + * attack if a number of packets are received with + * unreasonable packet sizes, noting that the maximum packet + * size is ~4GB. The CTDB protocol was not designed with a + * maximum packet size in mind, so CTDB may send very large + * valid packets. This means that imposing an arbitrary limit + * on incoming packets is not reasonable. Arguments that + * header fields, such as magic and/or version, should be + * validated before allocating a packet buffer are spurious + * from a security perspective because an attacker attempting + * DoS can send packets with valid headers. The private + * network and ctdbd socket should be secured against + * untrusted access, so reports that this possible DoS vector + * represents a security issue will be ignored. See the + * "Private addresses" section in ctdb(7) for more details. + */ if (state->use_fixed) { /* switch to dynamic buffer */ tmp = talloc_array(state, uint8_t, state->total + more); diff -Nru samba-4.22.10+dfsg/ctdb/common/sock_daemon.c samba-4.22.11+dfsg/ctdb/common/sock_daemon.c --- samba-4.22.10+dfsg/ctdb/common/sock_daemon.c 2025-01-30 13:57:43.590315800 +0000 +++ samba-4.22.11+dfsg/ctdb/common/sock_daemon.c 2026-07-21 15:37:04.179276000 +0000 @@ -204,6 +204,8 @@ static int socket_setup(const char *sockpath, bool remove_before_use) { struct sockaddr_un addr; + const char *t; + bool test_mode_enabled = false; size_t len; int ret, fd; @@ -239,6 +241,30 @@ close(fd); return -1; } + + t = getenv("CTDB_TEST_MODE"); + if (t != NULL) { + test_mode_enabled = true; + } + + if (!test_mode_enabled) { + /* Behaviour of fchown(2) is undefined on sockets */ + ret = chown(sockpath, geteuid(), getegid()); + if (ret != 0) { + D_ERR("Unable to secure (chown) socket '%s'\n", + sockpath); + close(fd); + return -1; + } + } + + /* Behaviour of fchmod(2) is undefined on sockets */ + ret = chmod(sockpath, 0700); + if (ret != 0) { + D_ERR("Unable to secure (chmod) socket '%s'\n", sockpath); + close(fd); + return -1; + } ret = listen(fd, 10); if (ret != 0) { diff -Nru samba-4.22.10+dfsg/ctdb/doc/ctdb.7.xml samba-4.22.11+dfsg/ctdb/doc/ctdb.7.xml --- samba-4.22.10+dfsg/ctdb/doc/ctdb.7.xml 2025-02-12 13:24:20.343143500 +0000 +++ samba-4.22.11+dfsg/ctdb/doc/ctdb.7.xml 2026-07-21 15:37:04.179276000 +0000 @@ -242,14 +242,25 @@ services provided by the cluster. - It is strongly recommended that the private addresses are - configured on a private network that is separate from client - networks. This is because the CTDB protocol is both - unauthenticated and unencrypted. If clients share the private - network then steps need to be taken to stop injection of - packets to relevant ports on the private addresses. It is - also likely that CTDB protocol traffic between nodes could - leak sensitive information if it can be intercepted. + It is strongly recommended that the private addresses are + configured on a private network that is separate from client + networks. This is because the CTDB protocol is both + unauthenticated and unencrypted. If clients share + the private network then steps need to be taken to stop + injection of packets to relevant ports on the private + addresses. It is also likely that CTDB protocol traffic + between nodes could leak sensitive information if it can be + intercepted or manipulated. + + + In summary, the private network should not be accessible by + untrusted sources. We can't say "must not" because this can't + be enforced by CTDB, so we can only make a strong + recommendation. However, for this reason, future bugs in the + CTDB protocol used over the private network will be fixed as + regular bugs without following the Samba security process. + The same is true of local CTDB protocols used for Unix domain + socket and other communication. diff -Nru samba-4.22.10+dfsg/ctdb/protocol/protocol_basic.c samba-4.22.11+dfsg/ctdb/protocol/protocol_basic.c --- samba-4.22.10+dfsg/ctdb/protocol/protocol_basic.c 2025-01-30 13:57:43.603315800 +0000 +++ samba-4.22.11+dfsg/ctdb/protocol/protocol_basic.c 2026-07-21 15:37:04.179276000 +0000 @@ -247,7 +247,8 @@ int ctdb_string_pull(uint8_t *buf, size_t buflen, TALLOC_CTX *mem_ctx, const char **out, size_t *npull) { - const char *str; + const char *str = NULL; + size_t len = 0; if (buflen > UINT32_MAX) { return EMSGSIZE; @@ -265,7 +266,14 @@ } *out = str; - *npull = ctdb_string_len(&str); + /* + * Avoid claiming to have consumed more than buflen. + * ctdb_string_len() returns buflen + 1 if there is no + * NUL-terminator within buflen, so no NUL was actually + * consumed (so no +1 needed). + */ + len = ctdb_string_len(&str); + *npull = MIN(buflen, len); return 0; } @@ -311,7 +319,8 @@ if (ret != 0) { return ret; } - offset += np; + /* Always consume the specified number bytes */ + offset += u32; *npull = offset; return 0; diff -Nru samba-4.22.10+dfsg/ctdb/protocol/protocol_types.c samba-4.22.11+dfsg/ctdb/protocol/protocol_types.c --- samba-4.22.10+dfsg/ctdb/protocol/protocol_types.c 2025-01-30 13:57:43.605316000 +0000 +++ samba-4.22.11+dfsg/ctdb/protocol/protocol_types.c 2026-07-21 15:37:04.179276000 +0000 @@ -838,6 +838,7 @@ size_t *npull) { struct ctdb_statistics_list *val; + struct ctdb_statistics dummy = {}; size_t offset = 0, np; int ret, i; @@ -863,6 +864,11 @@ goto done; } + if ((uint64_t)val->num * ctdb_statistics_len(&dummy) > buflen - offset) { + ret = EMSGSIZE; + goto fail; + } + val->stats = talloc_array(val, struct ctdb_statistics, val->num); if (val->stats == NULL) { ret = ENOMEM; @@ -925,6 +931,7 @@ struct ctdb_vnn_map *val; size_t offset = 0, np; uint32_t i; + uint32_t dummy = 0; int ret; val = talloc(mem_ctx, struct ctdb_vnn_map); @@ -950,6 +957,11 @@ goto done; } + if ((uint64_t)val->size * ctdb_uint32_len(&dummy) > buflen - offset) { + ret = EMSGSIZE; + goto fail; + } + val->map = talloc_array(val, uint32_t, val->size); if (val->map == NULL) { ret = ENOMEM; @@ -1082,6 +1094,7 @@ struct ctdb_dbid_map **out, size_t *npull) { struct ctdb_dbid_map *val; + struct ctdb_dbid dummy = {}; size_t offset = 0, np; uint32_t i; int ret; @@ -1102,6 +1115,11 @@ goto done; } + if ((uint64_t)val->num * ctdb_dbid_len(&dummy) > buflen - offset) { + ret = EMSGSIZE; + goto fail; + } + val->dbs = talloc_array(val, struct ctdb_dbid, val->num); if (val->dbs == NULL) { ret = ENOMEM; @@ -2362,6 +2380,7 @@ struct ctdb_connection_list **out, size_t *npull) { struct ctdb_connection_list *val; + struct ctdb_connection dummy = {}; size_t offset = 0, np; uint32_t i; int ret; @@ -2382,6 +2401,11 @@ goto done; } + if ((uint64_t)val->num * ctdb_connection_len(&dummy) > buflen - offset) { + ret = EMSGSIZE; + goto fail; + } + val->conn = talloc_array(val, struct ctdb_connection, val->num); if (val->conn == NULL) { ret = ENOMEM; @@ -3399,6 +3423,7 @@ struct ctdb_tickle_list **out, size_t *npull) { struct ctdb_tickle_list *val; + struct ctdb_connection dummy = {}; size_t offset = 0, np; uint32_t i; int ret; @@ -3426,6 +3451,11 @@ goto done; } + if ((uint64_t)val->num * ctdb_connection_len(&dummy) > buflen - offset) { + ret = EMSGSIZE; + goto fail; + } + val->conn = talloc_array(val, struct ctdb_connection, val->num); if (val->conn == NULL) { ret = ENOMEM; @@ -3744,6 +3774,7 @@ struct ctdb_public_ip_list **out, size_t *npull) { struct ctdb_public_ip_list *val; + struct ctdb_public_ip dummy = {}; size_t offset = 0, np; uint32_t i; int ret; @@ -3764,6 +3795,11 @@ goto done; } + if ((uint64_t)val->num * ctdb_public_ip_len(&dummy) > buflen - offset) { + ret = EMSGSIZE; + goto fail; + } + val->ip = talloc_array(val, struct ctdb_public_ip, val->num); if (val->ip == NULL) { ret = ENOMEM; @@ -3899,6 +3935,7 @@ struct ctdb_node_map **out, size_t *npull) { struct ctdb_node_map *val; + struct ctdb_node_and_flags dummy = {}; size_t offset = 0, np; uint32_t i; int ret; @@ -3919,6 +3956,12 @@ goto done; } + if ((uint64_t)val->num * ctdb_node_and_flags_len(&dummy) > + buflen - offset) { + ret = EMSGSIZE; + goto fail; + } + val->node = talloc_array(val, struct ctdb_node_and_flags, val->num); if (val->node == NULL) { ret = ENOMEM; @@ -4099,6 +4142,7 @@ struct ctdb_script_list **out, size_t *npull) { struct ctdb_script_list *val; + struct ctdb_script dummy = {}; size_t offset = 0, np; uint32_t i; int ret; @@ -4132,6 +4176,12 @@ goto done; } + if ((uint64_t)val->num_scripts * ctdb_script_len(&dummy) + > buflen - offset) { + ret = EMSGSIZE; + goto fail; + } + val->script = talloc_array(val, struct ctdb_script, val->num_scripts); if (val->script == NULL) { ret = ENOMEM; @@ -4375,6 +4425,7 @@ struct ctdb_iface_list **out, size_t *npull) { struct ctdb_iface_list *val; + struct ctdb_iface dummy = {}; size_t offset = 0, np; uint32_t i; int ret; @@ -4395,6 +4446,11 @@ goto done; } + if ((uint64_t)val->num * ctdb_iface_len(&dummy) > buflen - offset) { + ret = EMSGSIZE; + goto fail; + } + val->iface = talloc_array(val, struct ctdb_iface, val->num); if (val->iface == NULL) { ret = ENOMEM; @@ -5322,6 +5378,10 @@ val->num = buflen / ctdb_g_lock_len(&lock); + /* + * No array count pre-check needed because val->num is + * calculated from buflen + */ val->lock = talloc_array(val, struct ctdb_g_lock, val->num); if (val->lock == NULL) { ret = ENOMEM; diff -Nru samba-4.22.10+dfsg/ctdb/server/ctdb_call.c samba-4.22.11+dfsg/ctdb/server/ctdb_call.c --- samba-4.22.10+dfsg/ctdb/server/ctdb_call.c 2025-01-30 13:57:43.606315900 +0000 +++ samba-4.22.11+dfsg/ctdb/server/ctdb_call.c 2026-07-21 15:37:04.179276000 +0000 @@ -583,6 +583,9 @@ void ctdb_request_dmaster(struct ctdb_context *ctdb, struct ctdb_req_header *hdr) { struct ctdb_req_dmaster_old *c = (struct ctdb_req_dmaster_old *)hdr; + size_t req_dmaster_header_len = offsetof(struct ctdb_req_dmaster_old, + data); + size_t buf_len = hdr->length; TDB_DATA key, data, data2; struct ctdb_ltdb_header header; struct ctdb_db_context *ctdb_db; @@ -590,6 +593,13 @@ size_t len; int ret; + if (buf_len < req_dmaster_header_len || + c->keylen > buf_len - req_dmaster_header_len || + c->datalen > buf_len - req_dmaster_header_len - c->keylen) { + DBG_WARNING("Invalid packet\n"); + return; + } + key.dptr = c->data; key.dsize = c->keylen; data.dptr = c->data + c->keylen; @@ -917,6 +927,8 @@ void ctdb_request_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr) { struct ctdb_req_call_old *c = (struct ctdb_req_call_old *)hdr; + size_t req_call_header_len = offsetof(struct ctdb_req_call_old, data); + size_t buf_len = hdr->length; TDB_DATA data; struct ctdb_reply_call_old *r; int ret, len; @@ -925,6 +937,13 @@ struct ctdb_db_context *ctdb_db; int tmp_count, bucket; + if (buf_len < req_call_header_len || + c->keylen > buf_len - req_call_header_len || + c->calldatalen > buf_len - req_call_header_len - c->keylen) { + DBG_WARNING("Invalid packet\n"); + return; + } + if (ctdb->methods == NULL) { DEBUG(DEBUG_INFO,(__location__ " Failed ctdb_request_call. Transport is DOWN\n")); return; @@ -1199,8 +1218,17 @@ void ctdb_reply_call(struct ctdb_context *ctdb, struct ctdb_req_header *hdr) { struct ctdb_reply_call_old *c = (struct ctdb_reply_call_old *)hdr; + size_t reply_call_header_len = offsetof(struct ctdb_reply_call_old, + data); + size_t buf_len = hdr->length; struct ctdb_call_state *state; + if (buf_len < reply_call_header_len || + c->datalen > buf_len - reply_call_header_len) { + DBG_WARNING("Invalid packet\n"); + return; + } + state = reqid_find(ctdb->idr, hdr->reqid, struct ctdb_call_state); if (state == NULL) { DEBUG(DEBUG_ERR, (__location__ " reqid %u not found\n", hdr->reqid)); @@ -1296,12 +1324,22 @@ void ctdb_reply_dmaster(struct ctdb_context *ctdb, struct ctdb_req_header *hdr) { struct ctdb_reply_dmaster_old *c = (struct ctdb_reply_dmaster_old *)hdr; + size_t reply_dmaster_header_len = offsetof(struct ctdb_reply_dmaster_old, + data); + size_t buf_len = hdr->length; struct ctdb_db_context *ctdb_db; TDB_DATA key, data; uint32_t record_flags = 0; size_t len; int ret; + if (buf_len < reply_dmaster_header_len || + c->keylen > buf_len - reply_dmaster_header_len || + c->datalen > buf_len - reply_dmaster_header_len - c->keylen) { + DBG_WARNING("Invalid packet\n"); + return; + } + ctdb_db = find_ctdb_db(ctdb, c->db_id); if (ctdb_db == NULL) { DEBUG(DEBUG_ERR,("Unknown db_id 0x%x in ctdb_reply_dmaster\n", c->db_id)); diff -Nru samba-4.22.10+dfsg/ctdb/server/ctdb_client.c samba-4.22.11+dfsg/ctdb/server/ctdb_client.c --- samba-4.22.10+dfsg/ctdb/server/ctdb_client.c 2025-01-30 13:57:43.607315800 +0000 +++ samba-4.22.11+dfsg/ctdb/server/ctdb_client.c 2026-07-21 15:37:04.179276000 +0000 @@ -193,8 +193,17 @@ struct ctdb_req_header *hdr) { struct ctdb_req_message_old *c = (struct ctdb_req_message_old *)hdr; + size_t req_message_header_len = offsetof(struct ctdb_req_message_old, + data); + size_t buf_len = hdr->length; TDB_DATA data; + if (buf_len < req_message_header_len || + c->datalen > buf_len - req_message_header_len) { + DBG_WARNING("Invalid packet\n"); + return; + } + data.dsize = c->datalen; data.dptr = talloc_memdup(c, &c->data[0], c->datalen); if (data.dptr == NULL) { diff -Nru samba-4.22.10+dfsg/ctdb/server/ctdb_control.c samba-4.22.11+dfsg/ctdb/server/ctdb_control.c --- samba-4.22.10+dfsg/ctdb/server/ctdb_control.c 2025-01-30 13:57:43.608316000 +0000 +++ samba-4.22.11+dfsg/ctdb/server/ctdb_control.c 2026-07-21 15:37:04.179276000 +0000 @@ -933,11 +933,20 @@ void ctdb_request_control(struct ctdb_context *ctdb, struct ctdb_req_header *hdr) { struct ctdb_req_control_old *c = (struct ctdb_req_control_old *)hdr; + size_t req_control_header_len = offsetof(struct ctdb_req_control_old, + data); + size_t buf_len = hdr->length; TDB_DATA data, *outdata; int32_t status; bool async_reply = false; const char *errormsg = NULL; + if (buf_len < req_control_header_len || + c->datalen > buf_len - req_control_header_len) { + DBG_WARNING("Invalid packet\n"); + return; + } + data.dptr = &c->data[0]; data.dsize = c->datalen; @@ -957,10 +966,20 @@ void ctdb_reply_control(struct ctdb_context *ctdb, struct ctdb_req_header *hdr) { struct ctdb_reply_control_old *c = (struct ctdb_reply_control_old *)hdr; + size_t reply_control_header_len = offsetof(struct ctdb_reply_control_old, + data); + size_t buf_len = hdr->length; TDB_DATA data; struct ctdb_control_state *state; const char *errormsg = NULL; + if (buf_len < reply_control_header_len || + c->datalen > buf_len - reply_control_header_len || + c->errorlen > buf_len - reply_control_header_len - c->datalen) { + DBG_WARNING("Invalid packet\n"); + return; + } + state = reqid_find(ctdb->idr, hdr->reqid, struct ctdb_control_state); if (state == NULL) { DEBUG(DEBUG_ERR,("pnn %u Invalid reqid %u in ctdb_reply_control\n", diff -Nru samba-4.22.10+dfsg/ctdb/server/ctdb_ltdb_server.c samba-4.22.11+dfsg/ctdb/server/ctdb_ltdb_server.c --- samba-4.22.10+dfsg/ctdb/server/ctdb_ltdb_server.c 2025-01-30 13:57:43.609315900 +0000 +++ samba-4.22.11+dfsg/ctdb/server/ctdb_ltdb_server.c 2026-07-21 15:37:04.179276000 +0000 @@ -1109,8 +1109,15 @@ struct ctdb_db_context *db; struct ctdb_node *node = ctdb->nodes[ctdb->pnn]; struct ctdb_client *client = NULL; + char *t = NULL; uint32_t opcode; + t = memchr(indata.dptr, '\0', indata.dsize); + if (t == NULL) { + DBG_ERR("Invalid packet\n"); + return -1; + } + if (ctdb->tunable.allow_client_db_attach == 0) { DEBUG(DEBUG_ERR, ("DB Attach to database %s denied by tunable " "AllowClientDBAccess == 0\n", db_name)); diff -Nru samba-4.22.10+dfsg/ctdb/server/ctdb_persistent.c samba-4.22.11+dfsg/ctdb/server/ctdb_persistent.c --- samba-4.22.10+dfsg/ctdb/server/ctdb_persistent.c 2025-01-30 13:57:43.610315800 +0000 +++ samba-4.22.11+dfsg/ctdb/server/ctdb_persistent.c 2026-07-21 15:37:04.179276000 +0000 @@ -182,6 +182,16 @@ struct ctdb_marshall_buffer *m = (struct ctdb_marshall_buffer *)recdata.dptr; struct ctdb_db_context *ctdb_db; + if (recdata.dsize < offsetof(struct ctdb_marshall_buffer, data)) { + DBG_ERR("Invalid packet\n"); + return -1; + } + if (m->count > + recdata.dsize - offsetof(struct ctdb_marshall_buffer, data)) { + DBG_ERR("Invalid packet\n"); + return -1; + } + if (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL) { DEBUG(DEBUG_INFO,("rejecting ctdb_control_trans3_commit when recovery active\n")); return -1; diff -Nru samba-4.22.10+dfsg/ctdb/server/ctdb_recover.c samba-4.22.11+dfsg/ctdb/server/ctdb_recover.c --- samba-4.22.10+dfsg/ctdb/server/ctdb_recover.c 2026-05-15 13:51:43.449060200 +0000 +++ samba-4.22.11+dfsg/ctdb/server/ctdb_recover.c 2026-07-21 15:37:04.179276000 +0000 @@ -36,6 +36,8 @@ #include "ctdb_private.h" #include "ctdb_client.h" +#include "protocol/protocol_private.h" + #include "common/system.h" #include "common/common.h" #include "common/logging.h" @@ -67,24 +69,32 @@ int ctdb_control_setvnnmap(struct ctdb_context *ctdb, uint32_t opcode, TDB_DATA indata, TDB_DATA *outdata) { - struct ctdb_vnn_map_wire *map = (struct ctdb_vnn_map_wire *)indata.dptr; + struct ctdb_vnn_map *new = NULL; + size_t npull = 0; + int ret = 0; if (ctdb->recovery_mode != CTDB_RECOVERY_ACTIVE) { DEBUG(DEBUG_ERR, ("Attempt to set vnnmap when not in recovery\n")); return -1; } - talloc_free(ctdb->vnn_map); - - ctdb->vnn_map = talloc(ctdb, struct ctdb_vnn_map); - CTDB_NO_MEMORY(ctdb, ctdb->vnn_map); - - ctdb->vnn_map->generation = map->generation; - ctdb->vnn_map->size = map->size; - ctdb->vnn_map->map = talloc_array(ctdb->vnn_map, uint32_t, map->size); - CTDB_NO_MEMORY(ctdb, ctdb->vnn_map->map); + ret = ctdb_vnn_map_pull(indata.dptr, indata.dsize, ctdb, &new, &npull); + if (ret != 0) { + switch (ret) { + case ENOMEM: + DBG_ERR("Memory allocation error\n"); + break; + case EMSGSIZE: + DBG_ERR("Invalid packet\n"); + break; + default: + DBG_ERR("Unexpected error (%d)\n", ret); + } + return -1; + } - memcpy(ctdb->vnn_map->map, map->map, sizeof(uint32_t)*map->size); + talloc_free(ctdb->vnn_map); + ctdb->vnn_map = new; return 0; } @@ -1052,6 +1062,12 @@ return -1; } + if (reply->count > + indata.dsize - offsetof(struct ctdb_marshall_buffer, data)) { + DBG_ERR("Invalid packet\n"); + return -1; + } + ctdb_db = find_ctdb_db(ctdb, reply->db_id); if (!ctdb_db) { DEBUG(DEBUG_ERR,(__location__ " Unknown db 0x%08x\n", reply->db_id)); diff -Nru samba-4.22.10+dfsg/ctdb/server/ctdb_traverse.c samba-4.22.11+dfsg/ctdb/server/ctdb_traverse.c --- samba-4.22.10+dfsg/ctdb/server/ctdb_traverse.c 2025-01-30 13:57:43.613315800 +0000 +++ samba-4.22.11+dfsg/ctdb/server/ctdb_traverse.c 2026-07-21 15:37:04.179276000 +0000 @@ -561,13 +561,17 @@ int32_t ctdb_control_traverse_data(struct ctdb_context *ctdb, TDB_DATA data, TDB_DATA *outdata) { struct ctdb_rec_data_old *d = (struct ctdb_rec_data_old *)data.dptr; + size_t rec_data_header_len = offsetof(struct ctdb_rec_data_old, data); struct ctdb_traverse_all_handle *state; TDB_DATA key; ctdb_traverse_fn_t callback; void *private_data; - if (data.dsize < sizeof(uint32_t) || data.dsize != d->length) { - DEBUG(DEBUG_ERR,("Bad record size in ctdb_control_traverse_data\n")); + if (data.dsize < rec_data_header_len || + data.dsize != d->length || + d->keylen > d->length - rec_data_header_len || + d->datalen > d->length - rec_data_header_len - d->keylen) { + DBG_ERR("Invalid packet\n"); return -1; } diff -Nru samba-4.22.10+dfsg/ctdb/server/ctdb_update_record.c samba-4.22.11+dfsg/ctdb/server/ctdb_update_record.c --- samba-4.22.10+dfsg/ctdb/server/ctdb_update_record.c 2025-01-30 13:57:43.614315700 +0000 +++ samba-4.22.11+dfsg/ctdb/server/ctdb_update_record.c 2026-07-21 15:37:04.179276000 +0000 @@ -318,6 +318,16 @@ struct childwrite_handle *handle; struct ctdb_marshall_buffer *m = (struct ctdb_marshall_buffer *)recdata.dptr; + if (recdata.dsize < offsetof(struct ctdb_marshall_buffer, data)) { + DBG_ERR("Invalid packet\n"); + return -1; + } + if (m->count > + recdata.dsize - offsetof(struct ctdb_marshall_buffer, data)) { + DBG_ERR("Invalid packet\n"); + return -1; + } + if (ctdb->recovery_mode != CTDB_RECOVERY_NORMAL) { DEBUG(DEBUG_INFO,("rejecting ctdb_control_update_record when recovery active\n")); return -1; diff -Nru samba-4.22.10+dfsg/debian/changelog samba-4.22.11+dfsg/debian/changelog --- samba-4.22.10+dfsg/debian/changelog 2026-05-26 12:46:55.000000000 +0000 +++ samba-4.22.11+dfsg/debian/changelog 2026-07-28 12:02:03.000000000 +0000 @@ -1,3 +1,44 @@ +samba (2:4.22.11+dfsg-0+deb13u1) trixie; urgency=medium + + * switch to actual new upstream release, dropping the security + patchset (it is included into the upstream tarball now). + + Besides all the security fixes, there's an addtional fix in + this release (which was queued before the security fixes): + + o libsecurity: Fix security_acl_dup()'s talloc hierarchy + Bug: https://bugzilla.samba.org/show_bug.cgi?id=16095 + + -- Michael Tokarev Tue, 28 Jul 2026 15:02:03 +0300 + +samba (2:4.22.10+dfsg-0+deb13u2) trixie-security; urgency=medium + + * 2026-jul-sec-update-bug-16039-v4-22-combined.patch: + Jul-2026 samba security update addresses the following defects: + + CVE-2026-6949: https://bugzilla.samba.org/show_bug.cgi?id=16083 + TSIG packet with crafted name compression can crash internal DNS server + + CVE-2026-58224: https://bugzilla.samba.org/show_bug.cgi?id=16085 + CTDB: heap OOB read via unchecked packet length fields + + CVE-2026-58216: https://bugzilla.samba.org/show_bug.cgi?id=16087 + kpasswd service: 6-byte heap OOB read in packet parser + + CVE-2026-58218: https://bugzilla.samba.org/show_bug.cgi?id=16115 + DNS TKEY negotiation stores unauthenticated GSS contexts + in a fixed FIFO before authentication completes + + CVE-2026-58221: https://bugzilla.samba.org/show_bug.cgi?id=16147 + authenticated LDAP access to internal LDB special DNs + permits domain takeover + + CVE-2026-58222: https://bugzilla.samba.org/show_bug.cgi?id=16148 + LDAP Compare filter injection and trusted-request + confusion disclose protected attributes + + -- Michael Tokarev Fri, 24 Jul 2026 16:14:13 +0300 + samba (2:4.22.10+dfsg-0+deb13u1) trixie; urgency=medium * switch to actual upstream release for the May-2026 security fixes: diff -Nru samba-4.22.10+dfsg/libcli/security/security_descriptor.c samba-4.22.11+dfsg/libcli/security/security_descriptor.c --- samba-4.22.10+dfsg/libcli/security/security_descriptor.c 2025-02-12 13:24:20.370143400 +0000 +++ samba-4.22.11+dfsg/libcli/security/security_descriptor.c 2026-07-21 15:37:04.183276200 +0000 @@ -22,6 +22,7 @@ #include "replace.h" #include "libcli/security/security.h" #include "librpc/ndr/libndr.h" +#include "librpc/gen_ndr/ndr_security.h" /* return a blank security descriptor (no owners, dacl or sacl) @@ -53,6 +54,7 @@ const struct security_acl *oacl) { struct security_acl *nacl; + enum ndr_err_code ndr_err; if (oacl == NULL) { return NULL; @@ -67,17 +69,8 @@ return NULL; } - *nacl = (struct security_acl) { - .revision = oacl->revision, - .size = oacl->size, - .num_aces = oacl->num_aces, - }; - if (nacl->num_aces == 0) { - return nacl; - } - - nacl->aces = (struct security_ace *)talloc_memdup (nacl, oacl->aces, sizeof(struct security_ace) * oacl->num_aces); - if (nacl->aces == NULL) { + ndr_err = ndr_deepcopy_struct(security_acl, oacl, nacl, nacl); + if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { goto failed; } diff -Nru samba-4.22.10+dfsg/librpc/idl/dns.idl samba-4.22.11+dfsg/librpc/idl/dns.idl --- samba-4.22.10+dfsg/librpc/idl/dns.idl 2025-01-30 13:57:43.823315900 +0000 +++ samba-4.22.11+dfsg/librpc/idl/dns.idl 2026-07-21 15:37:04.183276200 +0000 @@ -251,6 +251,12 @@ } dns_rdata; typedef [flag(LIBNDR_PRINT_ARRAY_HEX|NDR_NOALIGN),nopush,nopull] struct { + /* + * This is the start offset of this + * dns_res_rec relative to the start of + * dns_name_packet buffer. + */ + [ignore] uint32 start_ndr_offset; dns_string name; dns_qtype rr_type; dns_qclass rr_class; diff -Nru samba-4.22.10+dfsg/librpc/ndr/ndr_dns.c samba-4.22.11+dfsg/librpc/ndr/ndr_dns.c --- samba-4.22.10+dfsg/librpc/ndr/ndr_dns.c 2025-01-30 13:57:43.834315800 +0000 +++ samba-4.22.11+dfsg/librpc/ndr/ndr_dns.c 2026-07-21 15:37:04.183276200 +0000 @@ -276,6 +276,17 @@ ndr_set_flags(&ndr->flags, LIBNDR_PRINT_ARRAY_HEX | LIBNDR_FLAG_NOALIGN); if (ndr_flags & NDR_SCALARS) { + /* + * Remember the start offset in order + * to know how to truncate before the + * last dns_res_rec in order to do + * the correct TSIG calculation. + * + * Note that we just set LIBNDR_FLAG_NOALIGN + * above, so ndr_pull_align is a no-op. + */ + r->start_ndr_offset = ndr->offset; + NDR_CHECK(ndr_pull_align(ndr, 4)); NDR_CHECK(ndr_pull_dns_string(ndr, NDR_SCALARS, &r->name)); NDR_CHECK(ndr_pull_dns_qtype(ndr, NDR_SCALARS, &r->rr_type)); diff -Nru samba-4.22.10+dfsg/source4/dns_server/dns_crypto.c samba-4.22.11+dfsg/source4/dns_server/dns_crypto.c --- samba-4.22.10+dfsg/source4/dns_server/dns_crypto.c 2025-01-30 13:57:44.144316000 +0000 +++ samba-4.22.11+dfsg/source4/dns_server/dns_crypto.c 2026-07-21 15:37:04.183276200 +0000 @@ -36,6 +36,8 @@ struct dns_res_rec *old, struct dns_res_rec *new_rec) { + new_rec->start_ndr_offset = 0; + new_rec->name = talloc_strdup(mem_ctx, old->name); W_ERROR_HAVE_NO_MEMORY(new_rec->name); @@ -101,7 +103,7 @@ NTSTATUS status; enum ndr_err_code ndr_err; uint16_t i, arcount = 0; - DATA_BLOB tsig_blob, fake_tsig_blob, sig; + DATA_BLOB fake_tsig_blob, sig; uint8_t *buffer = NULL; size_t buffer_len = 0, packet_len = 0; struct dns_server_tkey *tkey = NULL; @@ -131,6 +133,22 @@ state->sign = true; DBG_DEBUG("Got TSIG\n"); + /* + * We need to keep the input packet exactly like we got it, + * but we need to cut off the tsig record. + * + * DNS packets can not be larger than UINT16_MAX, + * the size of the UDP payload is uint16_t and + * there's a uint16_t length header for TCP. + * + * And the start offset of the last + * additional record can't be larger than + * the whole packet. + */ + packet_len = packet->additional[i].start_ndr_offset; + SMB_ASSERT(in->length <= UINT16_MAX); + SMB_ASSERT(in->length > packet_len); + state->tsig = talloc_zero(state->mem_ctx, struct dns_res_rec); if (state->tsig == NULL) { return WERR_NOT_ENOUGH_MEMORY; @@ -205,14 +223,6 @@ check_rec->other_size = 0; check_rec->other_data = NULL; - ndr_err = ndr_push_struct_blob(&tsig_blob, mem_ctx, state->tsig, - (ndr_push_flags_fn_t)ndr_push_dns_res_rec); - if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { - DEBUG(1, ("Failed to push packet: %s!\n", - ndr_errstr(ndr_err))); - return DNS_ERR(SERVER_FAILURE); - } - ndr_err = ndr_push_struct_blob(&fake_tsig_blob, mem_ctx, check_rec, (ndr_push_flags_fn_t)ndr_push_dns_fake_tsig_rec); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { @@ -221,9 +231,19 @@ return DNS_ERR(SERVER_FAILURE); } - /* we need to work some magic here. we need to keep the input packet - * exactly like we got it, but we need to cut off the tsig record */ - packet_len = in->length - tsig_blob.length; + /* + * We already asserted packet_len < UINT16_MAX + * above, and struct ndr_push has alloc_size and + * offset as uint32_t, so it's really unlikely + * to overflow buffer_len. For SIZE_MAX == UINT32_MAX, + * the following check might be important, but + * just lets do it always. + */ + if (fake_tsig_blob.length > (SIZE_MAX - UINT16_MAX)) { + DBG_WARNING("fake_tsig_blob.length=%zu too large!\n", + fake_tsig_blob.length); + return DNS_ERR(SERVER_FAILURE); + } buffer_len = packet_len + fake_tsig_blob.length; buffer = talloc_zero_array(mem_ctx, uint8_t, buffer_len); if (buffer == NULL) { diff -Nru samba-4.22.10+dfsg/source4/dns_server/dns_query.c samba-4.22.11+dfsg/source4/dns_server/dns_query.c --- samba-4.22.10+dfsg/source4/dns_server/dns_query.c 2025-01-30 13:57:44.144316000 +0000 +++ samba-4.22.11+dfsg/source4/dns_server/dns_query.c 2026-07-21 15:37:04.183276200 +0000 @@ -654,7 +654,8 @@ return WERR_OK; } -static NTSTATUS create_tkey(struct dns_server *dns, +static NTSTATUS create_tkey(TALLOC_CTX *mem_ctx, + struct dns_server *dns, const char* name, const char* algorithm, const struct tsocket_address *remote_address, @@ -662,7 +663,6 @@ struct dns_server_tkey **tkey) { NTSTATUS status; - struct dns_server_tkey_store *store = dns->tkeys; struct dns_server_tkey *k = NULL; if (strcmp(algorithm, "gss-tsig") == 0) { @@ -673,20 +673,20 @@ return NT_STATUS_ACCESS_DENIED; } - k = talloc_zero(store, struct dns_server_tkey); + k = talloc_zero(mem_ctx, struct dns_server_tkey); if (k == NULL) { - return NT_STATUS_NO_MEMORY; + goto nomem; } k->name = talloc_strdup(k, name); if (k->name == NULL) { - return NT_STATUS_NO_MEMORY; + goto nomem; } k->algorithm = talloc_strdup(k, algorithm); if (k->algorithm == NULL) { - return NT_STATUS_NO_MEMORY; + goto nomem; } /* @@ -704,8 +704,7 @@ &k->gensec); if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status))); - *tkey = NULL; - return status; + goto fail; } gensec_want_feature(k->gensec, GENSEC_FEATURE_SIGN); @@ -715,8 +714,7 @@ if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to set remote address into GENSEC: %s\n", nt_errstr(status))); - *tkey = NULL; - return status; + goto fail; } status = gensec_set_local_address(k->gensec, @@ -724,8 +722,7 @@ if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to set local address into GENSEC: %s\n", nt_errstr(status))); - *tkey = NULL; - return status; + goto fail; } status = gensec_start_mech_by_oid(k->gensec, GENSEC_OID_SPNEGO); @@ -733,18 +730,29 @@ if (!NT_STATUS_IS_OK(status)) { DEBUG(1, ("Failed to start GENSEC server code: %s\n", nt_errstr(status))); - *tkey = NULL; - return status; + goto fail; } + *tkey = k; + return NT_STATUS_OK; + +nomem: + status = NT_STATUS_NO_MEMORY; +fail: + TALLOC_FREE(k); + *tkey = NULL; + return status; +} + +static void add_tkey(struct dns_server_tkey_store *store, + struct dns_server_tkey **tkey) +{ TALLOC_FREE(store->tkeys[store->next_idx]); - store->tkeys[store->next_idx] = k; + store->tkeys[store->next_idx] = talloc_move(store->tkeys, tkey); + (store->next_idx)++; store->next_idx %= store->size; - - *tkey = k; - return NT_STATUS_OK; } static NTSTATUS accept_gss_ticket(TALLOC_CTX *mem_ctx, @@ -860,6 +868,7 @@ struct dns_server_tkey *tkey; DATA_BLOB key; DATA_BLOB reply; + bool created = false; tkey = dns_find_tkey(dns->tkeys, in->questions[0].name); if (tkey != NULL && tkey->complete) { @@ -870,15 +879,19 @@ } if (tkey == NULL) { - status = create_tkey(dns, in->questions[0].name, - in_tkey->rdata.tkey_record.algorithm, - state->remote_address, - state->local_address, - &tkey); + status = create_tkey( + mem_ctx, + dns, + in->questions[0].name, + in_tkey->rdata.tkey_record.algorithm, + state->remote_address, + state->local_address, + &tkey); if (!NT_STATUS_IS_OK(status)) { ret_tkey->rdata.tkey_record.error = DNS_RCODE_BADKEY; return ntstatus_to_werror(status); } + created = true; } key.data = in_tkey->rdata.tkey_record.key_data; @@ -903,6 +916,9 @@ if (state->key_name == NULL) { return WERR_NOT_ENOUGH_MEMORY; } + if (created) { + add_tkey(dns->tkeys, &tkey); + } } else { DEBUG(1, ("GSS key negotiation returned %s\n", nt_errstr(status))); ret_tkey->rdata.tkey_record.error = DNS_RCODE_BADKEY; diff -Nru samba-4.22.10+dfsg/source4/dsdb/samdb/ldb_modules/audit_util.c samba-4.22.11+dfsg/source4/dsdb/samdb/ldb_modules/audit_util.c --- samba-4.22.10+dfsg/source4/dsdb/samdb/ldb_modules/audit_util.c 2025-01-30 13:57:44.154316000 +0000 +++ samba-4.22.11+dfsg/source4/dsdb/samdb/ldb_modules/audit_util.c 2026-07-21 15:37:04.183276200 +0000 @@ -694,3 +694,183 @@ DBG_ERR("Unable to create ldb attributes JSON audit message\n"); return attributes; } + +/* + * @brief Generate a human readable string, detailing attributes in a message + * + * For modify operations each attribute is prefixed with the action. + * Normal values are enclosed in [] + * Base64 values are enclosed in {} + * Truncated values are indicated by three trailing dots "..." + * + * @param[in] ldb the ldb_context + * @param[out] buffer the attributes will be appended to the buffer. + * assumed to have been allocated via talloc. + * @param[in] operation the operation type + * @param[in] message the message to process + * + * @return a pointer to buffer + * + */ +char *dsdb_audit_log_attributes( + struct ldb_context *ldb, + char *buffer, + enum ldb_request_type operation, + const struct ldb_message *message) +{ + size_t i, j; + for (i=0;inum_elements;i++) { + if (i > 0) { + buffer = talloc_asprintf_append_buffer(buffer, " "); + } + + if (message->elements[i].name == NULL) { + ldb_debug( + ldb, + LDB_DEBUG_ERROR, + "Error: Invalid element name (NULL) at " + "position %zu", i); + return NULL; + } + + if (operation == LDB_MODIFY) { + const char *action =NULL; + action = dsdb_audit_get_modification_action( + message->elements[i].flags); + buffer = talloc_asprintf_append_buffer( + buffer, + "%s: %s ", + action, + message->elements[i].name); + } else { + buffer = talloc_asprintf_append_buffer( + buffer, + "%s ", + message->elements[i].name); + } + + if (dsdb_audit_redact_attribute(message->elements[i].name)) { + /* + * Do not log the value of any secret or password + * attributes + */ + buffer = talloc_asprintf_append_buffer( + buffer, + "[REDACTED SECRET ATTRIBUTE]"); + continue; + } + + for (j=0;jelements[i].num_values;j++) { + struct ldb_val v; + bool use_b64_encode = false; + size_t length; + if (j > 0) { + buffer = talloc_asprintf_append_buffer( + buffer, + " "); + } + + v = message->elements[i].values[j]; + length = MIN(MAX_LENGTH, v.length); + use_b64_encode = ldb_should_b64_encode(ldb, &v); + if (use_b64_encode) { + const char *encoded = ldb_base64_encode( + buffer, + (char *)v.data, + length); + buffer = talloc_asprintf_append_buffer( + buffer, + "{%s%s}", + encoded, + (v.length > MAX_LENGTH ? "..." : "")); + } else { + buffer = talloc_asprintf_append_buffer( + buffer, + "[%*.*s%s]", + (int)length, + (int)length, + (char *)v.data, + (v.length > MAX_LENGTH ? "..." : "")); + } + } + } + return buffer; +} + +/* + * @brief generate a human readable log entry detailing an ldb operation. + * + * Generate a human readable log entry detailing an ldb operation. + * + * @param[in] mem_ctx the talloc context owning the returned string. + * @param[in] module the ldb module + * @param[in] request the request + * @param[in] reply the result of the operation + * + * @return the log entry. + * + */ +char *dsdb_audit_operation_human_readable( + TALLOC_CTX *mem_ctx, + struct ldb_module *module, + const struct ldb_request *request, + const struct ldb_reply *reply) +{ + struct ldb_context *ldb = NULL; + const char *remote_host = NULL; + const struct tsocket_address *remote = NULL; + const struct dom_sid *sid = NULL; + struct dom_sid_buf user_sid; + const char *timestamp = NULL; + const char *op_name = NULL; + char *log_entry = NULL; + const char *dn = NULL; + const char *new_dn = NULL; + const struct ldb_message *message = NULL; + + TALLOC_CTX *ctx = talloc_new(NULL); + + ldb = ldb_module_get_ctx(module); + + remote_host = dsdb_audit_get_remote_host(ldb, ctx); + remote = dsdb_audit_get_remote_address(ldb); + if (remote != NULL && dsdb_audit_is_system_session(module)) { + sid = dsdb_audit_get_actual_sid(ldb); + } else { + sid = dsdb_audit_get_user_sid(module); + } + timestamp = audit_get_timestamp(ctx); + op_name = dsdb_audit_get_operation_name(request); + dn = dsdb_audit_get_primary_dn(request); + new_dn = dsdb_audit_get_secondary_dn(request); + + message = dsdb_audit_get_message(request); + + log_entry = talloc_asprintf( + mem_ctx, + "[%s] at [%s] status [%s] " + "remote host [%s] SID [%s] DN [%s]", + op_name, + timestamp, + ldb_strerror(reply->error), + remote_host, + dom_sid_str_buf(sid, &user_sid), + dn); + if (new_dn != NULL) { + log_entry = talloc_asprintf_append_buffer( + log_entry, + " New DN [%s]", + new_dn); + } + if (message != NULL) { + log_entry = talloc_asprintf_append_buffer(log_entry, + " attributes ["); + log_entry = dsdb_audit_log_attributes(ldb, + log_entry, + request->operation, + message); + log_entry = talloc_asprintf_append_buffer(log_entry, "]"); + } + TALLOC_FREE(ctx); + return log_entry; +} diff -Nru samba-4.22.10+dfsg/source4/dsdb/samdb/ldb_modules/rootdse.c samba-4.22.11+dfsg/source4/dsdb/samdb/ldb_modules/rootdse.c --- samba-4.22.10+dfsg/source4/dsdb/samdb/ldb_modules/rootdse.c 2025-01-30 13:57:44.161316000 +0000 +++ samba-4.22.11+dfsg/source4/dsdb/samdb/ldb_modules/rootdse.c 2026-07-21 15:37:04.183276200 +0000 @@ -28,6 +28,7 @@ #include "dsdb/samdb/samdb.h" #include "version.h" #include "dsdb/samdb/ldb_modules/util.h" +#include "dsdb/samdb/ldb_modules/audit_util_proto.h" #include "libcli/security/security.h" #include "librpc/ndr/libndr.h" #include "auth/auth.h" @@ -746,18 +747,68 @@ return LDB_SUCCESS; } -/* Ensure that anonymous users are not allowed to make anything other than rootDSE search operations */ - +/* + * Ensure that anonymous users are not allowed to make anything other than + * rootDSE search operations and special dns like @MODULES are not allowed + * over an untrusted connection. + */ static int rootdse_filter_operations(struct ldb_module *module, struct ldb_request *req) { struct auth_session_info *session_info; struct rootdse_private_data *priv = talloc_get_type(ldb_module_get_private(module), struct rootdse_private_data); bool is_untrusted = ldb_req_is_untrusted(req); bool is_anonymous = true; + struct ldb_dn *dn = NULL; + struct ldb_dn *dn2 = NULL; + if (is_untrusted == false) { return LDB_SUCCESS; } + switch (req->operation) { + case LDB_SEARCH: + dn = req->op.search.base; + break; + case LDB_ADD: + dn = req->op.add.message->dn; + break; + case LDB_MODIFY: + dn = req->op.mod.message->dn; + break; + case LDB_DELETE: + dn = req->op.del.dn; + break; + case LDB_RENAME: + dn = req->op.rename.olddn; + dn2 = req->op.rename.newdn; + break; + case LDB_EXTENDED: + break; + case LDB_REQ_REGISTER_CONTROL: + case LDB_REQ_REGISTER_PARTITION: + ldb_set_errstring(ldb_module_get_ctx(module), "Invalid OP"); + return LDB_ERR_OPERATIONS_ERROR; + } + + if (ldb_dn_is_special(dn)) { + struct ldb_reply reply = { .error = LDB_ERR_OPERATIONS_ERROR, }; + + D_ERR("CVE-2026-58221-ATTACK: %s\n", + dsdb_audit_operation_human_readable(req, module, req, &reply)); + + ldb_set_errstring(ldb_module_get_ctx(module), "Invalid DN"); + return LDB_ERR_OPERATIONS_ERROR; + } + if (ldb_dn_is_special(dn2)) { + struct ldb_reply reply = { .error = LDB_ERR_OPERATIONS_ERROR, }; + + D_ERR("CVE-2026-58221-ATTACK: %s\n", + dsdb_audit_operation_human_readable(req, module, req, &reply)); + + ldb_set_errstring(ldb_module_get_ctx(module), "Invalid DN"); + return LDB_ERR_OPERATIONS_ERROR; + } + session_info = (struct auth_session_info *)ldb_get_opaque( ldb_module_get_ctx(module), DSDB_SESSION_INFO); diff -Nru samba-4.22.10+dfsg/source4/dsdb/samdb/ldb_modules/wscript_build_server samba-4.22.11+dfsg/source4/dsdb/samdb/ldb_modules/wscript_build_server --- samba-4.22.10+dfsg/source4/dsdb/samdb/ldb_modules/wscript_build_server 2025-06-04 09:08:46.438111500 +0000 +++ samba-4.22.11+dfsg/source4/dsdb/samdb/ldb_modules/wscript_build_server 2026-07-21 15:37:04.183276200 +0000 @@ -185,7 +185,7 @@ init_function='ldb_rootdse_module_init', module_init_name='ldb_init_module', internal_module=False, - deps='talloc samdb MESSAGING samba-security DSDB_MODULE_HELPERS RPC_NDR_IRPC' + deps='talloc samdb MESSAGING samba-security DSDB_MODULE_HELPERS DSDB_MODULE_HELPERS_AUDIT RPC_NDR_IRPC' ) diff -Nru samba-4.22.10+dfsg/source4/kdc/kpasswd-service.c samba-4.22.11+dfsg/source4/kdc/kpasswd-service.c --- samba-4.22.10+dfsg/source4/kdc/kpasswd-service.c 2025-01-30 13:57:44.178315900 +0000 +++ samba-4.22.11+dfsg/source4/kdc/kpasswd-service.c 2026-07-21 15:37:04.183276200 +0000 @@ -137,7 +137,7 @@ ap_req_blob = data_blob_const(&request->data[HEADER_LEN], ap_req_len); - enc_data_len = len - ap_req_len; + enc_data_len = len - (ap_req_len + HEADER_LEN); enc_data_blob = data_blob_const(&request->data[HEADER_LEN + ap_req_len], enc_data_len); diff -Nru samba-4.22.10+dfsg/source4/ldap_server/ldap_backend.c samba-4.22.11+dfsg/source4/ldap_server/ldap_backend.c --- samba-4.22.10+dfsg/source4/ldap_server/ldap_backend.c 2025-01-30 13:57:44.187316000 +0000 +++ samba-4.22.11+dfsg/source4/ldap_server/ldap_backend.c 2026-07-21 15:37:04.183276200 +0000 @@ -29,6 +29,7 @@ #include "samba/service_stream.h" #include "dsdb/gmsa/util.h" #include "dsdb/samdb/samdb.h" +#include "dsdb/common/util.h" #include #include #include "ldb_wrap.h" @@ -1449,6 +1450,7 @@ struct ldb_dn *dn; const char *attrs[1]; const char *errstr = NULL; + const char *value = NULL; const char *filter = NULL; int result = LDAP_SUCCESS; int ldb_ret; @@ -1461,21 +1463,31 @@ dn = ldb_dn_new(local_ctx, samdb, req->dn); NT_STATUS_HAVE_NO_MEMORY(dn); + compare_r = ldapsrv_init_reply(call, LDAP_TAG_CompareResponse); + NT_STATUS_HAVE_NO_MEMORY(compare_r); + DBG_DEBUG("dn: [%s]\n", req->dn); - filter = talloc_asprintf(local_ctx, "(%s=%*s)", req->attribute, - (int)req->value.length, req->value.data); + + if (!ldb_valid_attr_name(req->attribute)) { + result = LDAP_INVALID_ATTRIBUTE_SYNTAX; + errstr = "Invalid Compare attribute name"; + goto reply; + } + value = ldb_binary_encode(local_ctx, req->value); + NT_STATUS_HAVE_NO_MEMORY(value); + + filter = talloc_asprintf(local_ctx, "%s=%s", req->attribute, value); NT_STATUS_HAVE_NO_MEMORY(filter); DBG_DEBUG("attribute: [%s]\n", filter); attrs[0] = NULL; - compare_r = ldapsrv_init_reply(call, LDAP_TAG_CompareResponse); - NT_STATUS_HAVE_NO_MEMORY(compare_r); - if (result == LDAP_SUCCESS) { - ldb_ret = ldb_search(samdb, local_ctx, &res, - dn, LDB_SCOPE_BASE, attrs, "%s", filter); + ldb_ret = dsdb_search(samdb, local_ctx, &res, + dn, LDB_SCOPE_BASE, attrs, + DSDB_MARK_REQ_UNTRUSTED, + "%s", filter); if (ldb_ret != LDB_SUCCESS) { result = map_ldb_error(local_ctx, ldb_ret, ldb_errstring(samdb), &errstr); @@ -1497,6 +1509,7 @@ } } +reply: compare = &compare_r->msg->r.CompareResponse; compare->dn = NULL; compare->resultcode = result; diff -Nru samba-4.22.10+dfsg/source4/librpc/tests/dns-decode_dns_name_packet-hex.txt samba-4.22.11+dfsg/source4/librpc/tests/dns-decode_dns_name_packet-hex.txt --- samba-4.22.10+dfsg/source4/librpc/tests/dns-decode_dns_name_packet-hex.txt 2025-01-30 13:57:44.210316000 +0000 +++ samba-4.22.11+dfsg/source4/librpc/tests/dns-decode_dns_name_packet-hex.txt 2026-07-21 15:37:04.183276200 +0000 @@ -21,6 +21,7 @@ answers: ARRAY(0) nsrecs: ARRAY(1) nsrecs: struct dns_res_rec + start_ndr_offset : 0x00000027 (39) name : 'cnamedotprefix0.samba2003.example.com' rr_type : DNS_QTYPE_CNAME (0x5) rr_class : DNS_QCLASS_IN (0x1)