aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYasuhiro Kimura <yasu@FreeBSD.org>2022-09-26 23:47:17 +0000
committerYasuhiro Kimura <yasu@FreeBSD.org>2022-10-11 05:32:29 +0000
commitdfaa714fc960fb4d0a5bc6983d3882a974857f12 (patch)
tree4574c193768381a192e53a2500aefd22fe4919d3
parentfe9d45ce9c2b680476f3647ba27111bd5d3adf7a (diff)
downloadports-dfaa714fc960fb4d0a5bc6983d3882a974857f12.tar.gz
ports-dfaa714fc960fb4d0a5bc6983d3882a974857f12.zip
net/samba413: Backport security fix from 4.14.14
* Add upstream patch to fix configure error with Python 3.11. * Fix plist error when AD_DC option is off and PYTHON3 option is on. * Replace BIND911 option with BIND918 as dns/bind911 is removed from ports tree and dns/bind918 is added instead. PR: 266641 Approved by: maintainer timeout MFH: 2022Q4 Security: f9140ad4-4920-11ed-a07e-080027f5fec9
-rw-r--r--net/samba413/Makefile8
-rw-r--r--net/samba413/files/patch-samba-4.14.1413366
-rw-r--r--net/samba413/files/patch-waf-2.0.201663
-rw-r--r--net/samba413/files/patch-waf-2.0.21703
-rw-r--r--net/samba413/files/patch-waf-2.0.22596
-rw-r--r--net/samba413/files/patch-waf-2.0.23877
-rw-r--r--net/samba413/files/patch-waf-2.0.24164
-rw-r--r--net/samba413/pkg-plist.ad_dc2
-rw-r--r--net/samba413/pkg-plist.python3
9 files changed, 17376 insertions, 6 deletions
diff --git a/net/samba413/Makefile b/net/samba413/Makefile
index f1dea1c15f3d..ccbb438ecb6a 100644
--- a/net/samba413/Makefile
+++ b/net/samba413/Makefile
@@ -1,6 +1,6 @@
PORTNAME= ${SAMBA4_BASENAME}413
PORTVERSION= ${SAMBA4_VERSION}
-PORTREVISION= 1
+PORTREVISION= 2
CATEGORIES?= net
MASTER_SITES= SAMBA/samba/stable SAMBA/samba/rc
DISTNAME= ${SAMBA4_DISTNAME}
@@ -96,7 +96,7 @@ OPTIONS_SINGLE_GSSAPI= GSSAPI_BUILTIN GSSAPI_MIT
OPTIONS_SINGLE_ZEROCONF= ZEROCONF_NONE AVAHI MDNSRESPONDER
OPTIONS_RADIO= DNS
-OPTIONS_RADIO_DNS= NSUPDATE BIND911 BIND916
+OPTIONS_RADIO_DNS= NSUPDATE BIND916 BIND918
# Make those default options
OPTIONS_DEFAULT= AD_DC ADS DOCS FAM LDAP \
PROFILE PYTHON3 QUOTAS SYSLOG UTMP \
@@ -129,8 +129,8 @@ ZEROCONF_DESC= Zero configuration networking
ZEROCONF_NONE_DESC= Zeroconf support is absent
DNS_DESC= DNS frontend
-BIND911_DESC= Use Bind 9.11 as AD DC DNS server frontend
BIND916_DESC= Use Bind 9.16 as AD DC DNS server frontend
+BIND918_DESC= Use Bind 9.18 as AD DC DNS server frontend
NSUPDATE_DESC= Use samba NSUPDATE utility for AD DC
##############################################################################
# XXX: Unconditional dependencies which can't be switched off(if present in
@@ -299,8 +299,8 @@ MDNSRESPONDER_CONFIGURE_ENABLE= dnssd
MDNSRESPONDER_LIB_DEPENDS= libdns_sd.so:net/mDNSResponder
MDNSRESPONDER_VARS= SAMBA4_SERVICES+=mdnsd
##############################################################################
-BIND911_RUN_DEPENDS= bind911>=9.11.0.0:dns/bind911
BIND916_RUN_DEPENDS= bind916>=9.16.0.0:dns/bind916
+BIND918_RUN_DEPENDS= bind918>=9.18.0.0:dns/bind918
NSUPDATE_RUN_DEPENDS= samba-nsupdate:dns/samba-nsupdate
##############################################################################
MEMORY_DEBUG_IMPLIES= DEBUG
diff --git a/net/samba413/files/patch-samba-4.14.14 b/net/samba413/files/patch-samba-4.14.14
new file mode 100644
index 000000000000..4127ab67308e
--- /dev/null
+++ b/net/samba413/files/patch-samba-4.14.14
@@ -0,0 +1,13366 @@
+From 5d958156c7e5d6c1da61d18fe4fd105b22639b56 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 14 Jun 2022 21:09:53 +1200
+Subject: [PATCH 01/99] CVE-2022-32746 s4/dsdb/objectclass_attrs: Fix typo
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/objectclass_attrs.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git source4/dsdb/samdb/ldb_modules/objectclass_attrs.c source4/dsdb/samdb/ldb_modules/objectclass_attrs.c
+index 6ab46a729a2..2a77353cdfc 100644
+--- source4/dsdb/samdb/ldb_modules/objectclass_attrs.c
++++ source4/dsdb/samdb/ldb_modules/objectclass_attrs.c
+@@ -263,7 +263,7 @@ static int attr_handler(struct oc_context *ac)
+ LDB_CONTROL_AS_SYSTEM_OID);
+ if (!dsdb_module_am_system(ac->module) && !as_system) {
+ ldb_asprintf_errstring(ldb,
+- "objectclass_attrs: attribute '%s' on entry '%s' must can only be modified as system",
++ "objectclass_attrs: attribute '%s' on entry '%s' can only be modified as system",
+ msg->elements[i].name,
+ ldb_dn_get_linearized(msg->dn));
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+--
+2.25.1
+
+
+From 51cbeff886fe01db463448f8655a43d10040dc8b Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 21 Jun 2022 15:37:15 +1200
+Subject: [PATCH 02/99] CVE-2022-32746 s4:dsdb:tests: Add test for deleting a
+ disallowed SPN
+
+If an account has an SPN that requires Write Property to set, we should
+still be able to delete it with just Validated Write.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ selftest/knownfail.d/acl-spn-delete | 1 +
+ source4/dsdb/tests/python/acl.py | 26 ++++++++++++++++++++++++++
+ 2 files changed, 27 insertions(+)
+ create mode 100644 selftest/knownfail.d/acl-spn-delete
+
+diff --git selftest/knownfail.d/acl-spn-delete selftest/knownfail.d/acl-spn-delete
+new file mode 100644
+index 00000000000..32018413c49
+--- /dev/null
++++ selftest/knownfail.d/acl-spn-delete
+@@ -0,0 +1 @@
++^samba4.ldap.acl.python.*__main__.AclSPNTests.test_delete_disallowed_spn\(
+diff --git source4/dsdb/tests/python/acl.py source4/dsdb/tests/python/acl.py
+index df0fe12bf29..d90d3b3923f 100755
+--- source4/dsdb/tests/python/acl.py
++++ source4/dsdb/tests/python/acl.py
+@@ -2286,6 +2286,32 @@ class AclSPNTests(AclTests):
+ else:
+ self.fail(f'able to add disallowed SPN {not_allowed_spn}')
+
++ def test_delete_disallowed_spn(self):
++ # Grant Validated-SPN property.
++ mod = f'(OA;;SW;{security.GUID_DRS_VALIDATE_SPN};;{self.user_sid1})'
++ self.sd_utils.dacl_add_ace(self.computerdn, mod)
++
++ spn_base = f'HOST/{self.computername}'
++
++ not_allowed_spn = f'{spn_base}/{self.dcctx.get_domain_name()}'
++
++ # Add a disallowed SPN as admin.
++ msg = Message(Dn(self.ldb_admin, self.computerdn))
++ msg['servicePrincipalName'] = MessageElement(not_allowed_spn,
++ FLAG_MOD_ADD,
++ 'servicePrincipalName')
++ self.ldb_admin.modify(msg)
++
++ # Ensure we are able to delete a disallowed SPN.
++ msg = Message(Dn(self.ldb_user1, self.computerdn))
++ msg['servicePrincipalName'] = MessageElement(not_allowed_spn,
++ FLAG_MOD_DELETE,
++ 'servicePrincipalName')
++ try:
++ self.ldb_user1.modify(msg)
++ except LdbError:
++ self.fail(f'unable to delete disallowed SPN {not_allowed_spn}')
++
+
+ # tests SEC_ADS_LIST vs. SEC_ADS_LIST_OBJECT
+ @DynamicTestCase
+--
+2.25.1
+
+
+From a68553792a8512a2d266bbb86f064f78b5482a65 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 21 Jun 2022 14:41:02 +1200
+Subject: [PATCH 03/99] CVE-2022-32746 s4/dsdb/partition: Fix LDB flags
+ comparison
+
+LDB_FLAG_MOD_* values are not actually flags, and the previous
+comparison was equivalent to
+
+(req_msg->elements[el_idx].flags & LDB_FLAG_MOD_MASK) != 0
+
+which is true whenever any of the LDB_FLAG_MOD_* values are set. Correct
+the expression to what it was probably intended to be.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/partition.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git source4/dsdb/samdb/ldb_modules/partition.c source4/dsdb/samdb/ldb_modules/partition.c
+index 2544a106d13..2d90ca5d1b3 100644
+--- source4/dsdb/samdb/ldb_modules/partition.c
++++ source4/dsdb/samdb/ldb_modules/partition.c
+@@ -493,8 +493,8 @@ static int partition_copy_all_callback_action(
+ * them here too
+ */
+ for (el_idx=0; el_idx < req_msg->num_elements; el_idx++) {
+- if (req_msg->elements[el_idx].flags & LDB_FLAG_MOD_DELETE
+- || ((req_msg->elements[el_idx].flags & LDB_FLAG_MOD_REPLACE) &&
++ if (LDB_FLAG_MOD_TYPE(req_msg->elements[el_idx].flags) == LDB_FLAG_MOD_DELETE
++ || ((LDB_FLAG_MOD_TYPE(req_msg->elements[el_idx].flags) == LDB_FLAG_MOD_REPLACE) &&
+ req_msg->elements[el_idx].num_values == 0)) {
+ if (ldb_msg_find_element(modify_msg,
+ req_msg->elements[el_idx].name) != NULL) {
+--
+2.25.1
+
+
+From 582ac171364f0c28f54eaf4f21b5bfa7569b5233 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 21 Jun 2022 14:49:51 +1200
+Subject: [PATCH 04/99] CVE-2022-32746 s4:torture: Fix LDB flags comparison
+
+LDB_FLAG_MOD_* values are not actually flags, and the previous
+comparison was equivalent to
+
+(el->flags & LDB_FLAG_MOD_MASK) == 0
+
+which is only true if none of the LDB_FLAG_MOD_* values are set. Correct
+the expression to what it was probably intended to be.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ source4/torture/drs/rpc/dssync.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git source4/torture/drs/rpc/dssync.c source4/torture/drs/rpc/dssync.c
+index cde9f78692b..ff7ce2d9074 100644
+--- source4/torture/drs/rpc/dssync.c
++++ source4/torture/drs/rpc/dssync.c
+@@ -527,7 +527,9 @@ static bool test_analyse_objects(struct torture_context *tctx,
+ el = &new_msg->elements[idx];
+ a = dsdb_attribute_by_lDAPDisplayName(ldap_schema,
+ el->name);
+- if (!(el->flags & (LDB_FLAG_MOD_ADD|LDB_FLAG_MOD_REPLACE))) {
++ if (LDB_FLAG_MOD_TYPE(el->flags) != LDB_FLAG_MOD_ADD &&
++ LDB_FLAG_MOD_TYPE(el->flags) != LDB_FLAG_MOD_REPLACE)
++ {
+ /* DRS only value */
+ is_warning = false;
+ } else if (a->linkID & 1) {
+--
+2.25.1
+
+
+From 0526d27e9eddd9c2a54434cf0dcdb136a6c659e4 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 21 Jun 2022 15:22:47 +1200
+Subject: [PATCH 05/99] CVE-2022-32746 s4/dsdb/acl: Fix LDB flags comparison
+
+LDB_FLAG_MOD_* values are not actually flags, and the previous
+comparison was equivalent to
+
+(el->flags & LDB_FLAG_MOD_MASK) == 0
+
+which is only true if none of the LDB_FLAG_MOD_* values are set, so we
+would not successfully return if the element was a DELETE. Correct the
+expression to what it was intended to be.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ selftest/knownfail.d/acl-spn-delete | 1 -
+ source4/dsdb/samdb/ldb_modules/acl.c | 5 +++--
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+ delete mode 100644 selftest/knownfail.d/acl-spn-delete
+
+diff --git selftest/knownfail.d/acl-spn-delete selftest/knownfail.d/acl-spn-delete
+deleted file mode 100644
+index 32018413c49..00000000000
+--- selftest/knownfail.d/acl-spn-delete
++++ /dev/null
+@@ -1 +0,0 @@
+-^samba4.ldap.acl.python.*__main__.AclSPNTests.test_delete_disallowed_spn\(
+diff --git source4/dsdb/samdb/ldb_modules/acl.c source4/dsdb/samdb/ldb_modules/acl.c
+index 21e83276bfd..8016a2d4bd0 100644
+--- source4/dsdb/samdb/ldb_modules/acl.c
++++ source4/dsdb/samdb/ldb_modules/acl.c
+@@ -734,8 +734,9 @@ static int acl_check_spn(TALLOC_CTX *mem_ctx,
+ * If not add or replace (eg delete),
+ * return success
+ */
+- if ((el->flags
+- & (LDB_FLAG_MOD_ADD|LDB_FLAG_MOD_REPLACE)) == 0) {
++ if (LDB_FLAG_MOD_TYPE(el->flags) != LDB_FLAG_MOD_ADD &&
++ LDB_FLAG_MOD_TYPE(el->flags) != LDB_FLAG_MOD_REPLACE)
++ {
+ talloc_free(tmp_ctx);
+ return LDB_SUCCESS;
+ }
+--
+2.25.1
+
+
+From 2869b5aa3148869edf0d079266542aef6e64608e Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 16 Feb 2022 12:43:52 +1300
+Subject: [PATCH 06/99] CVE-2022-32746 ldb:rdn_name: Use LDB_FLAG_MOD_TYPE()
+ for flags equality check
+
+Now unrelated flags will no longer affect the result.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ lib/ldb/modules/rdn_name.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git lib/ldb/modules/rdn_name.c lib/ldb/modules/rdn_name.c
+index e69ad9315ae..25cffe07591 100644
+--- lib/ldb/modules/rdn_name.c
++++ lib/ldb/modules/rdn_name.c
+@@ -545,7 +545,7 @@ static int rdn_name_modify(struct ldb_module *module, struct ldb_request *req)
+ if (e != NULL) {
+ ldb_asprintf_errstring(ldb, "Modify of 'distinguishedName' on %s not permitted, must use 'rename' operation instead",
+ ldb_dn_get_linearized(req->op.mod.message->dn));
+- if (e->flags == LDB_FLAG_MOD_REPLACE) {
++ if (LDB_FLAG_MOD_TYPE(e->flags) == LDB_FLAG_MOD_REPLACE) {
+ return LDB_ERR_CONSTRAINT_VIOLATION;
+ } else {
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+--
+2.25.1
+
+
+From 535b5a366a2ad054f729e57e282e402cf13b2efc Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 14 Jun 2022 19:49:19 +1200
+Subject: [PATCH 07/99] CVE-2022-32746 s4/dsdb/repl_meta_data: Use
+ LDB_FLAG_MOD_TYPE() for flags equality check
+
+Now unrelated flags will no longer affect the result.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git source4/dsdb/samdb/ldb_modules/repl_meta_data.c source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+index ab506cec488..29ffda75c87 100644
+--- source4/dsdb/samdb/ldb_modules/repl_meta_data.c
++++ source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+@@ -3525,7 +3525,7 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
+ return ldb_module_operr(module);
+ }
+
+- if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_REPLACE) {
++ if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[0].flags) != LDB_FLAG_MOD_REPLACE) {
+ return ldb_module_operr(module);
+ }
+
+@@ -3558,11 +3558,11 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
+ return ldb_module_operr(module);
+ }
+
+- if (req->op.mod.message->elements[0].flags != LDB_FLAG_MOD_DELETE) {
++ if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[0].flags) != LDB_FLAG_MOD_DELETE) {
+ return ldb_module_operr(module);
+ }
+
+- if (req->op.mod.message->elements[1].flags != LDB_FLAG_MOD_ADD) {
++ if (LDB_FLAG_MOD_TYPE(req->op.mod.message->elements[1].flags) != LDB_FLAG_MOD_ADD) {
+ return ldb_module_operr(module);
+ }
+
+@@ -3645,7 +3645,7 @@ static int replmd_modify(struct ldb_module *module, struct ldb_request *req)
+ return ldb_module_operr(module);
+ }
+
+- if (msg->elements[0].flags != LDB_FLAG_MOD_ADD) {
++ if (LDB_FLAG_MOD_TYPE(msg->elements[0].flags) != LDB_FLAG_MOD_ADD) {
+ talloc_free(ac);
+ return ldb_module_operr(module);
+ }
+--
+2.25.1
+
+
+From bedd0b768c3f92645af033399aefd7ee971d9150 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 14 Jun 2022 21:11:33 +1200
+Subject: [PATCH 08/99] CVE-2022-32746 s4/dsdb/tombstone_reanimate: Use
+ LDB_FLAG_MOD_TYPE() for flags equality check
+
+Now unrelated flags will no longer affect the result.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c
+index 64e05195798..5f8911c66be 100644
+--- source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c
++++ source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c
+@@ -104,7 +104,7 @@ static bool is_tombstone_reanimate_request(struct ldb_request *req,
+ if (el_dn == NULL) {
+ return false;
+ }
+- if (el_dn->flags != LDB_FLAG_MOD_REPLACE) {
++ if (LDB_FLAG_MOD_TYPE(el_dn->flags) != LDB_FLAG_MOD_REPLACE) {
+ return false;
+ }
+ if (el_dn->num_values != 1) {
+@@ -117,7 +117,7 @@ static bool is_tombstone_reanimate_request(struct ldb_request *req,
+ return false;
+ }
+
+- if (el_deleted->flags != LDB_FLAG_MOD_DELETE) {
++ if (LDB_FLAG_MOD_TYPE(el_deleted->flags) != LDB_FLAG_MOD_DELETE) {
+ return false;
+ }
+
+--
+2.25.1
+
+
+From 49dd9042f4ee380fa1dafcebcb54d0e1f0852463 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 14 Jun 2022 21:12:39 +1200
+Subject: [PATCH 09/99] CVE-2022-32746 s4/registry: Use LDB_FLAG_MOD_TYPE() for
+ flags equality check
+
+Now unrelated flags will no longer affect the result.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ source4/lib/registry/ldb.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git source4/lib/registry/ldb.c source4/lib/registry/ldb.c
+index e089355975b..db383a560da 100644
+--- source4/lib/registry/ldb.c
++++ source4/lib/registry/ldb.c
+@@ -859,7 +859,7 @@ static WERROR ldb_set_value(struct hive_key *parent,
+
+ /* Try first a "modify" and if this doesn't work do try an "add" */
+ for (i = 0; i < msg->num_elements; i++) {
+- if (msg->elements[i].flags != LDB_FLAG_MOD_DELETE) {
++ if (LDB_FLAG_MOD_TYPE(msg->elements[i].flags) != LDB_FLAG_MOD_DELETE) {
+ msg->elements[i].flags = LDB_FLAG_MOD_REPLACE;
+ }
+ }
+--
+2.25.1
+
+
+From faa61ab3053d077ac9d0aa67e955217e85b660f4 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Mon, 21 Feb 2022 16:10:32 +1300
+Subject: [PATCH 10/99] CVE-2022-32746 ldb: Add flag to mark message element
+ values as shared
+
+When making a shallow copy of an ldb message, mark the message elements
+of the copy as sharing their values with the message elements in the
+original message.
+
+This flag value will be heeded in the next commit.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ lib/ldb/common/ldb_msg.c | 43 +++++++++++++++++++++++++++++++-----
+ lib/ldb/include/ldb_module.h | 6 +++++
+ 2 files changed, 43 insertions(+), 6 deletions(-)
+
+diff --git lib/ldb/common/ldb_msg.c lib/ldb/common/ldb_msg.c
+index 57dfc5a04c2..2a9ce384bb9 100644
+--- lib/ldb/common/ldb_msg.c
++++ lib/ldb/common/ldb_msg.c
+@@ -833,11 +833,7 @@ void ldb_msg_sort_elements(struct ldb_message *msg)
+ ldb_msg_element_compare_name);
+ }
+
+-/*
+- shallow copy a message - copying only the elements array so that the caller
+- can safely add new elements without changing the message
+-*/
+-struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx,
++static struct ldb_message *ldb_msg_copy_shallow_impl(TALLOC_CTX *mem_ctx,
+ const struct ldb_message *msg)
+ {
+ struct ldb_message *msg2;
+@@ -863,6 +859,35 @@ failed:
+ return NULL;
+ }
+
++/*
++ shallow copy a message - copying only the elements array so that the caller
++ can safely add new elements without changing the message
++*/
++struct ldb_message *ldb_msg_copy_shallow(TALLOC_CTX *mem_ctx,
++ const struct ldb_message *msg)
++{
++ struct ldb_message *msg2;
++ unsigned int i;
++
++ msg2 = ldb_msg_copy_shallow_impl(mem_ctx, msg);
++ if (msg2 == NULL) {
++ return NULL;
++ }
++
++ for (i = 0; i < msg2->num_elements; ++i) {
++ /*
++ * Mark this message's elements as sharing their values with the
++ * original message, so that we don't inadvertently modify or
++ * free them. We don't mark the original message element as
++ * shared, so the original message element should not be
++ * modified or freed while the shallow copy lives.
++ */
++ struct ldb_message_element *el = &msg2->elements[i];
++ el->flags |= LDB_FLAG_INTERNAL_SHARED_VALUES;
++ }
++
++ return msg2;
++}
+
+ /*
+ copy a message, allocating new memory for all parts
+@@ -873,7 +898,7 @@ struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx,
+ struct ldb_message *msg2;
+ unsigned int i, j;
+
+- msg2 = ldb_msg_copy_shallow(mem_ctx, msg);
++ msg2 = ldb_msg_copy_shallow_impl(mem_ctx, msg);
+ if (msg2 == NULL) return NULL;
+
+ if (msg2->dn != NULL) {
+@@ -894,6 +919,12 @@ struct ldb_message *ldb_msg_copy(TALLOC_CTX *mem_ctx,
+ goto failed;
+ }
+ }
++
++ /*
++ * Since we copied this element's values, we can mark them as
++ * not shared.
++ */
++ el->flags &= ~LDB_FLAG_INTERNAL_SHARED_VALUES;
+ }
+
+ return msg2;
+diff --git lib/ldb/include/ldb_module.h lib/ldb/include/ldb_module.h
+index 8c1e5ee7936..4c7c85a17f0 100644
+--- lib/ldb/include/ldb_module.h
++++ lib/ldb/include/ldb_module.h
+@@ -96,6 +96,12 @@ struct ldb_module;
+ */
+ #define LDB_FLAG_INTERNAL_FORCE_UNIQUE_INDEX 0x100
+
++/*
++ * indicates that this element's values are shared with another element (for
++ * example, in a shallow copy of an ldb_message) and should not be freed
++ */
++#define LDB_FLAG_INTERNAL_SHARED_VALUES 0x200
++
+ /* an extended match rule that always fails to match */
+ #define SAMBA_LDAP_MATCH_ALWAYS_FALSE "1.3.6.1.4.1.7165.4.5.1"
+
+--
+2.25.1
+
+
+From 4e5fb78c3dcff60aa8fd4b07dad4660bbb30532b Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 16 Feb 2022 12:35:13 +1300
+Subject: [PATCH 11/99] CVE-2022-32746 ldb: Ensure shallow copy modifications
+ do not affect original message
+
+Using the newly added ldb flag, we can now detect when a message has
+been shallow-copied so that its elements share their values with the
+original message elements. Then when adding values to the copied
+message, we now make a copy of the shared values array first.
+
+This should prevent a use-after-free that occurred in LDB modules when
+new values were added to a shallow copy of a message by calling
+talloc_realloc() on the original values array, invalidating the 'values'
+pointer in the original message element. The original values pointer can
+later be used in the database audit logging module which logs database
+requests, and potentially cause a crash.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ lib/ldb/common/ldb_msg.c | 52 ++++++++++++++++++++++++++++++++------
+ lib/ldb/include/ldb.h | 6 +++++
+ source4/dsdb/common/util.c | 20 +++++----------
+ 3 files changed, 56 insertions(+), 22 deletions(-)
+
+diff --git lib/ldb/common/ldb_msg.c lib/ldb/common/ldb_msg.c
+index 2a9ce384bb9..44d3b29e9a7 100644
+--- lib/ldb/common/ldb_msg.c
++++ lib/ldb/common/ldb_msg.c
+@@ -417,6 +417,47 @@ int ldb_msg_add(struct ldb_message *msg,
+ return LDB_SUCCESS;
+ }
+
++/*
++ * add a value to a message element
++ */
++int ldb_msg_element_add_value(TALLOC_CTX *mem_ctx,
++ struct ldb_message_element *el,
++ const struct ldb_val *val)
++{
++ struct ldb_val *vals;
++
++ if (el->flags & LDB_FLAG_INTERNAL_SHARED_VALUES) {
++ /*
++ * Another message is using this message element's values array,
++ * so we don't want to make any modifications to the original
++ * message, or potentially invalidate its own values by calling
++ * talloc_realloc(). Make a copy instead.
++ */
++ el->flags &= ~LDB_FLAG_INTERNAL_SHARED_VALUES;
++
++ vals = talloc_array(mem_ctx, struct ldb_val,
++ el->num_values + 1);
++ if (vals == NULL) {
++ return LDB_ERR_OPERATIONS_ERROR;
++ }
++
++ if (el->values != NULL) {
++ memcpy(vals, el->values, el->num_values * sizeof(struct ldb_val));
++ }
++ } else {
++ vals = talloc_realloc(mem_ctx, el->values, struct ldb_val,
++ el->num_values + 1);
++ if (vals == NULL) {
++ return LDB_ERR_OPERATIONS_ERROR;
++ }
++ }
++ el->values = vals;
++ el->values[el->num_values] = *val;
++ el->num_values++;
++
++ return LDB_SUCCESS;
++}
++
+ /*
+ add a value to a message
+ */
+@@ -426,7 +467,6 @@ int ldb_msg_add_value(struct ldb_message *msg,
+ struct ldb_message_element **return_el)
+ {
+ struct ldb_message_element *el;
+- struct ldb_val *vals;
+ int ret;
+
+ el = ldb_msg_find_element(msg, attr_name);
+@@ -437,14 +477,10 @@ int ldb_msg_add_value(struct ldb_message *msg,
+ }
+ }
+
+- vals = talloc_realloc(msg->elements, el->values, struct ldb_val,
+- el->num_values+1);
+- if (!vals) {
+- return LDB_ERR_OPERATIONS_ERROR;
++ ret = ldb_msg_element_add_value(msg->elements, el, val);
++ if (ret != LDB_SUCCESS) {
++ return ret;
+ }
+- el->values = vals;
+- el->values[el->num_values] = *val;
+- el->num_values++;
+
+ if (return_el) {
+ *return_el = el;
+diff --git lib/ldb/include/ldb.h lib/ldb/include/ldb.h
+index bc44157eaf4..129beefeaf5 100644
+--- lib/ldb/include/ldb.h
++++ lib/ldb/include/ldb.h
+@@ -1981,6 +1981,12 @@ int ldb_msg_add_empty(struct ldb_message *msg,
+ int flags,
+ struct ldb_message_element **return_el);
+
++/**
++ add a value to a message element
++*/
++int ldb_msg_element_add_value(TALLOC_CTX *mem_ctx,
++ struct ldb_message_element *el,
++ const struct ldb_val *val);
+ /**
+ add a element to a ldb_message
+ */
+diff --git source4/dsdb/common/util.c source4/dsdb/common/util.c
+index 5ce4c0a5e33..577b2a33873 100644
+--- source4/dsdb/common/util.c
++++ source4/dsdb/common/util.c
+@@ -816,7 +816,7 @@ int samdb_msg_add_addval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx,
+ const char *value)
+ {
+ struct ldb_message_element *el;
+- struct ldb_val val, *vals;
++ struct ldb_val val;
+ char *v;
+ unsigned int i;
+ bool found = false;
+@@ -851,14 +851,10 @@ int samdb_msg_add_addval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx,
+ }
+ }
+
+- vals = talloc_realloc(msg->elements, el->values, struct ldb_val,
+- el->num_values + 1);
+- if (vals == NULL) {
++ ret = ldb_msg_element_add_value(msg->elements, el, &val);
++ if (ret != LDB_SUCCESS) {
+ return ldb_oom(sam_ldb);
+ }
+- el->values = vals;
+- el->values[el->num_values] = val;
+- ++(el->num_values);
+
+ return LDB_SUCCESS;
+ }
+@@ -872,7 +868,7 @@ int samdb_msg_add_delval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx,
+ const char *value)
+ {
+ struct ldb_message_element *el;
+- struct ldb_val val, *vals;
++ struct ldb_val val;
+ char *v;
+ unsigned int i;
+ bool found = false;
+@@ -907,14 +903,10 @@ int samdb_msg_add_delval(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx,
+ }
+ }
+
+- vals = talloc_realloc(msg->elements, el->values, struct ldb_val,
+- el->num_values + 1);
+- if (vals == NULL) {
++ ret = ldb_msg_element_add_value(msg->elements, el, &val);
++ if (ret != LDB_SUCCESS) {
+ return ldb_oom(sam_ldb);
+ }
+- el->values = vals;
+- el->values[el->num_values] = val;
+- ++(el->num_values);
+
+ return LDB_SUCCESS;
+ }
+--
+2.25.1
+
+
+From 512a2617b1593bdc16caeeeda4312a581cbb34e9 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 16 Feb 2022 16:30:03 +1300
+Subject: [PATCH 12/99] CVE-2022-32746 ldb: Add functions for appending to an
+ ldb_message
+
+Currently, there are many places where we use ldb_msg_add_empty() to add
+an empty element to a message, and then call ldb_msg_add_value() or
+similar to add values to that element. However, this performs an
+unnecessary search of the message's elements to locate the new element.
+Moreover, if an element with the same attribute name already exists
+earlier in the message, the values will be added to that element,
+instead of to the intended newly added element.
+
+A similar pattern exists where we add values to a message, and then call
+ldb_msg_find_element() to locate that message element and sets its flags
+to (e.g.) LDB_FLAG_MOD_REPLACE. This also performs an unnecessary
+search, and may locate the wrong message element for setting the flags.
+
+To avoid these problems, add functions for appending a value to a
+message, so that a particular value can be added to the end of a message
+in a single operation.
+
+For ADD requests, it is important that no two message elements share the
+same attribute name, otherwise things will break. (Normally,
+ldb_msg_normalize() is called before processing the request to help
+ensure this.) Thus, we must be careful not to append an attribute to an
+ADD message, unless we are sure (e.g. through ldb_msg_find_element())
+that an existing element for that attribute is not present.
+
+These functions will be used in the next commit.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ lib/ldb/common/ldb_msg.c | 165 ++++++++++++++++++++++++++++++++++++++-
+ lib/ldb/include/ldb.h | 24 ++++++
+ 2 files changed, 185 insertions(+), 4 deletions(-)
+
+diff --git lib/ldb/common/ldb_msg.c lib/ldb/common/ldb_msg.c
+index 44d3b29e9a7..9cd7998e21c 100644
+--- lib/ldb/common/ldb_msg.c
++++ lib/ldb/common/ldb_msg.c
+@@ -509,12 +509,15 @@ int ldb_msg_add_steal_value(struct ldb_message *msg,
+
+
+ /*
+- add a string element to a message
++ add a string element to a message, specifying flags
+ */
+-int ldb_msg_add_string(struct ldb_message *msg,
+- const char *attr_name, const char *str)
++int ldb_msg_add_string_flags(struct ldb_message *msg,
++ const char *attr_name, const char *str,
++ int flags)
+ {
+ struct ldb_val val;
++ int ret;
++ struct ldb_message_element *el = NULL;
+
+ val.data = discard_const_p(uint8_t, str);
+ val.length = strlen(str);
+@@ -524,7 +527,25 @@ int ldb_msg_add_string(struct ldb_message *msg,
+ return LDB_SUCCESS;
+ }
+
+- return ldb_msg_add_value(msg, attr_name, &val, NULL);
++ ret = ldb_msg_add_value(msg, attr_name, &val, &el);
++ if (ret != LDB_SUCCESS) {
++ return ret;
++ }
++
++ if (flags != 0) {
++ el->flags = flags;
++ }
++
++ return LDB_SUCCESS;
++}
++
++/*
++ add a string element to a message
++*/
++int ldb_msg_add_string(struct ldb_message *msg,
++ const char *attr_name, const char *str)
++{
++ return ldb_msg_add_string_flags(msg, attr_name, str, 0);
+ }
+
+ /*
+@@ -586,6 +607,142 @@ int ldb_msg_add_fmt(struct ldb_message *msg,
+ return ldb_msg_add_steal_value(msg, attr_name, &val);
+ }
+
++static int ldb_msg_append_value_impl(struct ldb_message *msg,
++ const char *attr_name,
++ const struct ldb_val *val,
++ int flags,
++ struct ldb_message_element **return_el)
++{
++ struct ldb_message_element *el = NULL;
++ int ret;
++
++ ret = ldb_msg_add_empty(msg, attr_name, flags, &el);
++ if (ret != LDB_SUCCESS) {
++ return ret;
++ }
++
++ ret = ldb_msg_element_add_value(msg->elements, el, val);
++ if (ret != LDB_SUCCESS) {
++ return ret;
++ }
++
++ if (return_el != NULL) {
++ *return_el = el;
++ }
++
++ return LDB_SUCCESS;
++}
++
++/*
++ append a value to a message
++*/
++int ldb_msg_append_value(struct ldb_message *msg,
++ const char *attr_name,
++ const struct ldb_val *val,
++ int flags)
++{
++ return ldb_msg_append_value_impl(msg, attr_name, val, flags, NULL);
++}
++
++/*
++ append a value to a message, stealing it into the 'right' place
++*/
++int ldb_msg_append_steal_value(struct ldb_message *msg,
++ const char *attr_name,
++ struct ldb_val *val,
++ int flags)
++{
++ int ret;
++ struct ldb_message_element *el = NULL;
++
++ ret = ldb_msg_append_value_impl(msg, attr_name, val, flags, &el);
++ if (ret == LDB_SUCCESS) {
++ talloc_steal(el->values, val->data);
++ }
++ return ret;
++}
++
++/*
++ append a string element to a message, stealing it into the 'right' place
++*/
++int ldb_msg_append_steal_string(struct ldb_message *msg,
++ const char *attr_name, char *str,
++ int flags)
++{
++ struct ldb_val val;
++
++ val.data = (uint8_t *)str;
++ val.length = strlen(str);
++
++ if (val.length == 0) {
++ /* allow empty strings as non-existent attributes */
++ return LDB_SUCCESS;
++ }
++
++ return ldb_msg_append_steal_value(msg, attr_name, &val, flags);
++}
++
++/*
++ append a string element to a message
++*/
++int ldb_msg_append_string(struct ldb_message *msg,
++ const char *attr_name, const char *str, int flags)
++{
++ struct ldb_val val;
++
++ val.data = discard_const_p(uint8_t, str);
++ val.length = strlen(str);
++
++ if (val.length == 0) {
++ /* allow empty strings as non-existent attributes */
++ return LDB_SUCCESS;
++ }
++
++ return ldb_msg_append_value(msg, attr_name, &val, flags);
++}
++
++/*
++ append a DN element to a message
++ WARNING: this uses the linearized string from the dn, and does not
++ copy the string.
++*/
++int ldb_msg_append_linearized_dn(struct ldb_message *msg, const char *attr_name,
++ struct ldb_dn *dn, int flags)
++{
++ char *str = ldb_dn_alloc_linearized(msg, dn);
++
++ if (str == NULL) {
++ /* we don't want to have unknown DNs added */
++ return LDB_ERR_OPERATIONS_ERROR;
++ }
++
++ return ldb_msg_append_steal_string(msg, attr_name, str, flags);
++}
++
++/*
++ append a printf formatted element to a message
++*/
++int ldb_msg_append_fmt(struct ldb_message *msg, int flags,
++ const char *attr_name, const char *fmt, ...)
++{
++ struct ldb_val val;
++ va_list ap;
++ char *str = NULL;
++
++ va_start(ap, fmt);
++ str = talloc_vasprintf(msg, fmt, ap);
++ va_end(ap);
++
++ if (str == NULL) {
++ return LDB_ERR_OPERATIONS_ERROR;
++ }
++
++ val.data = (uint8_t *)str;
++ val.length = strlen(str);
++
++ return ldb_msg_append_steal_value(msg, attr_name, &val, flags);
++}
++
+ /*
+ compare two ldb_message_element structures
+ assumes case sensitive comparison
+diff --git lib/ldb/include/ldb.h lib/ldb/include/ldb.h
+index 129beefeaf5..63d8aedd672 100644
+--- lib/ldb/include/ldb.h
++++ lib/ldb/include/ldb.h
+@@ -2002,12 +2002,36 @@ int ldb_msg_add_steal_value(struct ldb_message *msg,
+ struct ldb_val *val);
+ int ldb_msg_add_steal_string(struct ldb_message *msg,
+ const char *attr_name, char *str);
++int ldb_msg_add_string_flags(struct ldb_message *msg,
++ const char *attr_name, const char *str,
++ int flags);
+ int ldb_msg_add_string(struct ldb_message *msg,
+ const char *attr_name, const char *str);
+ int ldb_msg_add_linearized_dn(struct ldb_message *msg, const char *attr_name,
+ struct ldb_dn *dn);
+ int ldb_msg_add_fmt(struct ldb_message *msg,
+ const char *attr_name, const char *fmt, ...) PRINTF_ATTRIBUTE(3,4);
++/**
++ append a element to a ldb_message
++*/
++int ldb_msg_append_value(struct ldb_message *msg,
++ const char *attr_name,
++ const struct ldb_val *val,
++ int flags);
++int ldb_msg_append_steal_value(struct ldb_message *msg,
++ const char *attr_name,
++ struct ldb_val *val,
++ int flags);
++int ldb_msg_append_steal_string(struct ldb_message *msg,
++ const char *attr_name, char *str,
++ int flags);
++int ldb_msg_append_string(struct ldb_message *msg,
++ const char *attr_name, const char *str,
++ int flags);
++int ldb_msg_append_linearized_dn(struct ldb_message *msg, const char *attr_name,
++ struct ldb_dn *dn, int flags);
++int ldb_msg_append_fmt(struct ldb_message *msg, int flags,
++ const char *attr_name, const char *fmt, ...) PRINTF_ATTRIBUTE(4,5);
+
+ /**
+ compare two message elements - return 0 on match
+--
+2.25.1
+
+
+From f419753d1c7a373fb32ffe20930a6e084e44b44d Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Mon, 21 Feb 2022 16:27:37 +1300
+Subject: [PATCH 13/99] CVE-2022-32746 ldb: Make use of functions for appending
+ to an ldb_message
+
+This aims to minimise usage of the error-prone pattern of searching for
+a just-added message element in order to make modifications to it (and
+potentially finding the wrong element).
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ lib/ldb/ldb_map/ldb_map.c | 5 +-
+ lib/ldb/ldb_map/ldb_map_inbound.c | 9 +-
+ lib/ldb/modules/rdn_name.c | 22 +---
+ source3/passdb/pdb_samba_dsdb.c | 14 +--
+ source4/dns_server/dnsserver_common.c | 12 +-
+ source4/dsdb/common/util.c | 114 ++++++++++++++----
+ source4/dsdb/samdb/ldb_modules/descriptor.c | 10 +-
+ source4/dsdb/samdb/ldb_modules/objectguid.c | 20 +--
+ .../dsdb/samdb/ldb_modules/partition_init.c | 14 +--
+ .../dsdb/samdb/ldb_modules/repl_meta_data.c | 24 +---
+ source4/dsdb/samdb/ldb_modules/samldb.c | 78 +++++-------
+ .../samdb/ldb_modules/tombstone_reanimate.c | 12 +-
+ source4/nbt_server/wins/winsdb.c | 13 +-
+ source4/rpc_server/lsa/dcesrv_lsa.c | 55 +++------
+ source4/winbind/idmap.c | 10 +-
+ 15 files changed, 183 insertions(+), 229 deletions(-)
+
+diff --git lib/ldb/ldb_map/ldb_map.c lib/ldb/ldb_map/ldb_map.c
+index b453dff80d2..c7b0c228631 100644
+--- lib/ldb/ldb_map/ldb_map.c
++++ lib/ldb/ldb_map/ldb_map.c
+@@ -946,10 +946,7 @@ struct ldb_request *map_build_fixup_req(struct map_context *ac,
+ if ( ! dn || ! ldb_dn_validate(msg->dn)) {
+ goto failed;
+ }
+- if (ldb_msg_add_empty(msg, IS_MAPPED, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+- goto failed;
+- }
+- if (ldb_msg_add_string(msg, IS_MAPPED, dn) != 0) {
++ if (ldb_msg_append_string(msg, IS_MAPPED, dn, LDB_FLAG_MOD_REPLACE) != 0) {
+ goto failed;
+ }
+
+diff --git lib/ldb/ldb_map/ldb_map_inbound.c lib/ldb/ldb_map/ldb_map_inbound.c
+index 324295737da..50b9427c26c 100644
+--- lib/ldb/ldb_map/ldb_map_inbound.c
++++ lib/ldb/ldb_map/ldb_map_inbound.c
+@@ -569,12 +569,9 @@ static int map_modify_do_local(struct map_context *ac)
+ /* No local record present, add it instead */
+ /* Add local 'IS_MAPPED' */
+ /* TODO: use GUIDs here instead */
+- if (ldb_msg_add_empty(ac->local_msg, IS_MAPPED,
+- LDB_FLAG_MOD_ADD, NULL) != 0) {
+- return LDB_ERR_OPERATIONS_ERROR;
+- }
+- ret = ldb_msg_add_linearized_dn(ac->local_msg, IS_MAPPED,
+- ac->remote_req->op.mod.message->dn);
++ ret = ldb_msg_append_linearized_dn(ac->local_msg, IS_MAPPED,
++ ac->remote_req->op.mod.message->dn,
++ LDB_FLAG_MOD_ADD);
+ if (ret != 0) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+diff --git lib/ldb/modules/rdn_name.c lib/ldb/modules/rdn_name.c
+index 25cffe07591..3cb62bf567b 100644
+--- lib/ldb/modules/rdn_name.c
++++ lib/ldb/modules/rdn_name.c
+@@ -308,16 +308,10 @@ static int rdn_rename_callback(struct ldb_request *req, struct ldb_reply *ares)
+ }
+ rdn_val = ldb_val_dup(msg, rdn_val_p);
+
+- if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
++ if (ldb_msg_append_value(msg, rdn_name, &rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
+ goto error;
+ }
+- if (ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL) != 0) {
+- goto error;
+- }
+- if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+- goto error;
+- }
+- if (ldb_msg_add_value(msg, "name", &rdn_val, NULL) != 0) {
++ if (ldb_msg_append_value(msg, "name", &rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
+ goto error;
+ }
+
+@@ -466,11 +460,7 @@ static int rdn_name_modify(struct ldb_module *module, struct ldb_request *req)
+ if (ret != 0) {
+ return ldb_module_oom(module);
+ }
+- ret = ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_ADD, NULL);
+- if (ret != 0) {
+- return ldb_module_oom(module);
+- }
+- ret = ldb_msg_add_value(msg, rdn_name, &rdn_val, NULL);
++ ret = ldb_msg_append_value(msg, rdn_name, &rdn_val, LDB_FLAG_MOD_ADD);
+ if (ret != 0) {
+ return ldb_module_oom(module);
+ }
+@@ -479,11 +469,7 @@ static int rdn_name_modify(struct ldb_module *module, struct ldb_request *req)
+ if (ret != 0) {
+ return ldb_module_oom(module);
+ }
+- ret = ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_ADD, NULL);
+- if (ret != 0) {
+- return ldb_module_oom(module);
+- }
+- ret = ldb_msg_add_value(msg, "name", &rdn_val, NULL);
++ ret = ldb_msg_append_value(msg, "name", &rdn_val, LDB_FLAG_MOD_ADD);
+ if (ret != 0) {
+ return ldb_module_oom(module);
+ }
+diff --git source3/passdb/pdb_samba_dsdb.c source3/passdb/pdb_samba_dsdb.c
+index 93e8f5bebe6..b2063825c04 100644
+--- source3/passdb/pdb_samba_dsdb.c
++++ source3/passdb/pdb_samba_dsdb.c
+@@ -2855,18 +2855,10 @@ static bool pdb_samba_dsdb_set_trusteddom_pw(struct pdb_methods *m,
+ }
+
+ msg->num_elements = 0;
+- ret = ldb_msg_add_empty(msg, "trustAuthOutgoing",
+- LDB_FLAG_MOD_REPLACE, NULL);
++ ret = ldb_msg_append_value(msg, "trustAuthOutgoing",
++ &new_val, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+- DEBUG(0, ("ldb_msg_add_empty() failed\n"));
+- TALLOC_FREE(tmp_ctx);
+- ldb_transaction_cancel(state->ldb);
+- return false;
+- }
+- ret = ldb_msg_add_value(msg, "trustAuthOutgoing",
+- &new_val, NULL);
+- if (ret != LDB_SUCCESS) {
+- DEBUG(0, ("ldb_msg_add_value() failed\n"));
++ DEBUG(0, ("ldb_msg_append_value() failed\n"));
+ TALLOC_FREE(tmp_ctx);
+ ldb_transaction_cancel(state->ldb);
+ return false;
+diff --git source4/dns_server/dnsserver_common.c source4/dns_server/dnsserver_common.c
+index bcb0d087faf..cb9a082ebf6 100644
+--- source4/dns_server/dnsserver_common.c
++++ source4/dns_server/dnsserver_common.c
+@@ -1092,15 +1092,9 @@ WERROR dns_common_replace(struct ldb_context *samdb,
+ }
+
+ if (was_tombstoned || become_tombstoned) {
+- ret = ldb_msg_add_empty(msg, "dNSTombstoned",
+- LDB_FLAG_MOD_REPLACE, NULL);
+- if (ret != LDB_SUCCESS) {
+- werr = DNS_ERR(SERVER_FAILURE);
+- goto exit;
+- }
+-
+- ret = ldb_msg_add_fmt(msg, "dNSTombstoned", "%s",
+- become_tombstoned ? "TRUE" : "FALSE");
++ ret = ldb_msg_append_fmt(msg, LDB_FLAG_MOD_REPLACE,
++ "dNSTombstoned", "%s",
++ become_tombstoned ? "TRUE" : "FALSE");
+ if (ret != LDB_SUCCESS) {
+ werr = DNS_ERR(SERVER_FAILURE);
+ goto exit;
+diff --git source4/dsdb/common/util.c source4/dsdb/common/util.c
+index 577b2a33873..10d6ea8883b 100644
+--- source4/dsdb/common/util.c
++++ source4/dsdb/common/util.c
+@@ -924,6 +924,16 @@ int samdb_msg_add_int(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct l
+ return ldb_msg_add_string(msg, attr_name, s);
+ }
+
++int samdb_msg_add_int_flags(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
++ const char *attr_name, int v, int flags)
++{
++ const char *s = talloc_asprintf(mem_ctx, "%d", v);
++ if (s == NULL) {
++ return ldb_oom(sam_ldb);
++ }
++ return ldb_msg_add_string_flags(msg, attr_name, s, flags);
++}
++
+ /*
+ * Add an unsigned int element to a message
+ *
+@@ -942,6 +952,12 @@ int samdb_msg_add_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct
+ return samdb_msg_add_int(sam_ldb, mem_ctx, msg, attr_name, (int)v);
+ }
+
++int samdb_msg_add_uint_flags(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
++ const char *attr_name, unsigned int v, int flags)
++{
++ return samdb_msg_add_int_flags(sam_ldb, mem_ctx, msg, attr_name, (int)v, flags);
++}
++
+ /*
+ add a (signed) int64_t element to a message
+ */
+@@ -973,6 +989,68 @@ int samdb_msg_add_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struc
+ return samdb_msg_add_int64(sam_ldb, mem_ctx, msg, attr_name, (int64_t)v);
+ }
+
++/*
++ append a int element to a message
++*/
++int samdb_msg_append_int(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
++ const char *attr_name, int v, int flags)
++{
++ const char *s = talloc_asprintf(mem_ctx, "%d", v);
++ if (s == NULL) {
++ return ldb_oom(sam_ldb);
++ }
++ return ldb_msg_append_string(msg, attr_name, s, flags);
++}
++
++/*
++ * Append an unsigned int element to a message
++ *
++ * The issue here is that we have not yet first cast to int32_t explicitly,
++ * before we cast to an signed int to printf() into the %d or cast to a
++ * int64_t before we then cast to a long long to printf into a %lld.
++ *
++ * There are *no* unsigned integers in Active Directory LDAP, even the RID
++ * allocations and ms-DS-Secondary-KrbTgt-Number are *signed* quantities.
++ * (See the schema, and the syntax definitions in schema_syntax.c).
++ *
++ */
++int samdb_msg_append_uint(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
++ const char *attr_name, unsigned int v, int flags)
++{
++ return samdb_msg_append_int(sam_ldb, mem_ctx, msg, attr_name, (int)v, flags);
++}
++
++/*
++ append a (signed) int64_t element to a message
++*/
++int samdb_msg_append_int64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
++ const char *attr_name, int64_t v, int flags)
++{
++ const char *s = talloc_asprintf(mem_ctx, "%lld", (long long)v);
++ if (s == NULL) {
++ return ldb_oom(sam_ldb);
++ }
++ return ldb_msg_append_string(msg, attr_name, s, flags);
++}
++
++/*
++ * Append an unsigned int64_t (uint64_t) element to a message
++ *
++ * The issue here is that we have not yet first cast to int32_t explicitly,
++ * before we cast to an signed int to printf() into the %d or cast to a
++ * int64_t before we then cast to a long long to printf into a %lld.
++ *
++ * There are *no* unsigned integers in Active Directory LDAP, even the RID
++ * allocations and ms-DS-Secondary-KrbTgt-Number are *signed* quantities.
++ * (See the schema, and the syntax definitions in schema_syntax.c).
++ *
++ */
++int samdb_msg_append_uint64(struct ldb_context *sam_ldb, TALLOC_CTX *mem_ctx, struct ldb_message *msg,
++ const char *attr_name, uint64_t v, int flags)
++{
++ return samdb_msg_append_int64(sam_ldb, mem_ctx, msg, attr_name, (int64_t)v, flags);
++}
++
+ /*
+ add a samr_Password element to a message
+ */
+@@ -2814,15 +2892,8 @@ NTSTATUS samdb_set_password_sid(struct ldb_context *ldb, TALLOC_CTX *mem_ctx,
+ tdo_msg->num_elements = 0;
+ TALLOC_FREE(tdo_msg->elements);
+
+- ret = ldb_msg_add_empty(tdo_msg, "trustAuthIncoming",
+- LDB_FLAG_MOD_REPLACE, NULL);
+- if (ret != LDB_SUCCESS) {
+- ldb_transaction_cancel(ldb);
+- TALLOC_FREE(frame);
+- return NT_STATUS_NO_MEMORY;
+- }
+- ret = ldb_msg_add_value(tdo_msg, "trustAuthIncoming",
+- &new_val, NULL);
++ ret = ldb_msg_append_value(tdo_msg, "trustAuthIncoming",
++ &new_val, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(ldb);
+ TALLOC_FREE(frame);
+@@ -3187,6 +3258,7 @@ int dsdb_find_guid_by_dn(struct ldb_context *ldb,
+ /*
+ adds the given GUID to the given ldb_message. This value is added
+ for the given attr_name (may be either "objectGUID" or "parentGUID").
++ This function is used in processing 'add' requests.
+ */
+ int dsdb_msg_add_guid(struct ldb_message *msg,
+ struct GUID *guid,
+@@ -5656,7 +5728,8 @@ int dsdb_user_obj_set_defaults(struct ldb_context *ldb,
+ }
+
+ /**
+- * Sets 'sAMAccountType on user object based on userAccountControl
++ * Sets 'sAMAccountType on user object based on userAccountControl.
++ * This function is used in processing both 'add' and 'modify' requests.
+ * @param ldb Current ldb_context
+ * @param usr_obj ldb_message representing User object
+ * @param user_account_control Value for userAccountControl flags
+@@ -5668,21 +5741,19 @@ int dsdb_user_obj_set_account_type(struct ldb_context *ldb, struct ldb_message *
+ {
+ int ret;
+ uint32_t account_type;
+- struct ldb_message_element *el;
+
+ account_type = ds_uf2atype(user_account_control);
+ if (account_type == 0) {
+ ldb_set_errstring(ldb, "dsdb: Unrecognized account type!");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+- ret = samdb_msg_add_uint(ldb, usr_obj, usr_obj,
+- "sAMAccountType",
+- account_type);
++ ret = samdb_msg_add_uint_flags(ldb, usr_obj, usr_obj,
++ "sAMAccountType",
++ account_type,
++ LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el = ldb_msg_find_element(usr_obj, "sAMAccountType");
+- el->flags = LDB_FLAG_MOD_REPLACE;
+
+ if (account_type_p) {
+ *account_type_p = account_type;
+@@ -5692,7 +5763,8 @@ int dsdb_user_obj_set_account_type(struct ldb_context *ldb, struct ldb_message *
+ }
+
+ /**
+- * Determine and set primaryGroupID based on userAccountControl value
++ * Determine and set primaryGroupID based on userAccountControl value.
++ * This function is used in processing both 'add' and 'modify' requests.
+ * @param ldb Current ldb_context
+ * @param usr_obj ldb_message representing User object
+ * @param user_account_control Value for userAccountControl flags
+@@ -5704,17 +5776,15 @@ int dsdb_user_obj_set_primary_group_id(struct ldb_context *ldb, struct ldb_messa
+ {
+ int ret;
+ uint32_t rid;
+- struct ldb_message_element *el;
+
+ rid = ds_uf2prim_group_rid(user_account_control);
+
+- ret = samdb_msg_add_uint(ldb, usr_obj, usr_obj,
+- "primaryGroupID", rid);
++ ret = samdb_msg_add_uint_flags(ldb, usr_obj, usr_obj,
++ "primaryGroupID", rid,
++ LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el = ldb_msg_find_element(usr_obj, "primaryGroupID");
+- el->flags = LDB_FLAG_MOD_REPLACE;
+
+ if (group_rid_p) {
+ *group_rid_p = rid;
+diff --git source4/dsdb/samdb/ldb_modules/descriptor.c source4/dsdb/samdb/ldb_modules/descriptor.c
+index daa08c2ebc7..4b01961dcb0 100644
+--- source4/dsdb/samdb/ldb_modules/descriptor.c
++++ source4/dsdb/samdb/ldb_modules/descriptor.c
+@@ -857,14 +857,8 @@ static int descriptor_modify(struct ldb_module *module, struct ldb_request *req)
+ return ldb_module_done(req, NULL, NULL, LDB_SUCCESS);
+ }
+
+- ret = ldb_msg_add_empty(msg, "nTSecurityDescriptor",
+- LDB_FLAG_MOD_REPLACE,
+- &sd_element);
+- if (ret != LDB_SUCCESS) {
+- return ldb_oom(ldb);
+- }
+- ret = ldb_msg_add_value(msg, "nTSecurityDescriptor",
+- sd, NULL);
++ ret = ldb_msg_append_value(msg, "nTSecurityDescriptor",
++ sd, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ldb_oom(ldb);
+ }
+diff --git source4/dsdb/samdb/ldb_modules/objectguid.c source4/dsdb/samdb/ldb_modules/objectguid.c
+index bc3260cf0d8..0fe995a5763 100644
+--- source4/dsdb/samdb/ldb_modules/objectguid.c
++++ source4/dsdb/samdb/ldb_modules/objectguid.c
+@@ -41,7 +41,6 @@
+ */
+ static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
+ {
+- struct ldb_message_element *el;
+ char *s;
+ int ret;
+
+@@ -54,16 +53,13 @@ static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+- ret = ldb_msg_add_string(msg, attr, s);
++ /* always set as replace. This works because on add ops, the flag
++ is ignored */
++ ret = ldb_msg_append_string(msg, attr, s, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+- el = ldb_msg_find_element(msg, attr);
+- /* always set as replace. This works because on add ops, the flag
+- is ignored */
+- el->flags = LDB_FLAG_MOD_REPLACE;
+-
+ return LDB_SUCCESS;
+ }
+
+@@ -73,23 +69,19 @@ static int add_time_element(struct ldb_message *msg, const char *attr, time_t t)
+ static int add_uint64_element(struct ldb_context *ldb, struct ldb_message *msg,
+ const char *attr, uint64_t v)
+ {
+- struct ldb_message_element *el;
+ int ret;
+
+ if (ldb_msg_find_element(msg, attr) != NULL) {
+ return LDB_SUCCESS;
+ }
+
+- ret = samdb_msg_add_uint64(ldb, msg, msg, attr, v);
++ /* always set as replace. This works because on add ops, the flag
++ is ignored */
++ ret = samdb_msg_append_uint64(ldb, msg, msg, attr, v, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+- el = ldb_msg_find_element(msg, attr);
+- /* always set as replace. This works because on add ops, the flag
+- is ignored */
+- el->flags = LDB_FLAG_MOD_REPLACE;
+-
+ return LDB_SUCCESS;
+ }
+
+diff --git source4/dsdb/samdb/ldb_modules/partition_init.c source4/dsdb/samdb/ldb_modules/partition_init.c
+index 58c65ccedd0..484b5bffb27 100644
+--- source4/dsdb/samdb/ldb_modules/partition_init.c
++++ source4/dsdb/samdb/ldb_modules/partition_init.c
+@@ -742,10 +742,6 @@ int partition_create(struct ldb_module *module, struct ldb_request *req)
+ }
+
+ mod_msg->dn = ldb_dn_new(mod_msg, ldb, DSDB_PARTITION_DN);
+- ret = ldb_msg_add_empty(mod_msg, DSDB_PARTITION_ATTR, LDB_FLAG_MOD_ADD, NULL);
+- if (ret != LDB_SUCCESS) {
+- return ret;
+- }
+
+ casefold_dn = ldb_dn_get_casefold(dn);
+
+@@ -785,18 +781,16 @@ int partition_create(struct ldb_module *module, struct ldb_request *req)
+ }
+ partition_record = talloc_asprintf(mod_msg, "%s:%s", casefold_dn, filename);
+
+- ret = ldb_msg_add_steal_string(mod_msg, DSDB_PARTITION_ATTR, partition_record);
++ ret = ldb_msg_append_steal_string(mod_msg, DSDB_PARTITION_ATTR, partition_record,
++ LDB_FLAG_MOD_ADD);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ if (ldb_request_get_control(req, DSDB_CONTROL_PARTIAL_REPLICA)) {
+ /* this new partition is a partial replica */
+- ret = ldb_msg_add_empty(mod_msg, "partialReplica", LDB_FLAG_MOD_ADD, NULL);
+- if (ret != LDB_SUCCESS) {
+- return ret;
+- }
+- ret = ldb_msg_add_fmt(mod_msg, "partialReplica", "%s", ldb_dn_get_linearized(dn));
++ ret = ldb_msg_append_fmt(mod_msg, LDB_FLAG_MOD_ADD,
++ "partialReplica", "%s", ldb_dn_get_linearized(dn));
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+diff --git source4/dsdb/samdb/ldb_modules/repl_meta_data.c source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+index 29ffda75c87..eec1e639856 100644
+--- source4/dsdb/samdb/ldb_modules/repl_meta_data.c
++++ source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+@@ -3888,22 +3888,12 @@ static int replmd_rename_callback(struct ldb_request *req, struct ldb_reply *are
+ ldb_operr(ldb));
+ }
+
+- if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
++ if (ldb_msg_append_value(msg, rdn_name, rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ ldb_oom(ldb));
+ }
+- if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
+- talloc_free(ares);
+- return ldb_module_done(ac->req, NULL, NULL,
+- ldb_oom(ldb));
+- }
+- if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+- talloc_free(ares);
+- return ldb_module_done(ac->req, NULL, NULL,
+- ldb_oom(ldb));
+- }
+- if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
++ if (ldb_msg_append_value(msg, "name", rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
+ talloc_free(ares);
+ return ldb_module_done(ac->req, NULL, NULL,
+ ldb_oom(ldb));
+@@ -5161,16 +5151,10 @@ static int replmd_name_modify(struct replmd_replicated_request *ar,
+ goto failed;
+ }
+
+- if (ldb_msg_add_empty(msg, rdn_name, LDB_FLAG_MOD_REPLACE, NULL) != 0) {
+- goto failed;
+- }
+- if (ldb_msg_add_value(msg, rdn_name, rdn_val, NULL) != 0) {
+- goto failed;
+- }
+- if (ldb_msg_add_empty(msg, "name", LDB_FLAG_MOD_REPLACE, NULL) != 0) {
++ if (ldb_msg_append_value(msg, rdn_name, rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
+ goto failed;
+ }
+- if (ldb_msg_add_value(msg, "name", rdn_val, NULL) != 0) {
++ if (ldb_msg_append_value(msg, "name", rdn_val, LDB_FLAG_MOD_REPLACE) != 0) {
+ goto failed;
+ }
+
+diff --git source4/dsdb/samdb/ldb_modules/samldb.c source4/dsdb/samdb/ldb_modules/samldb.c
+index 5fb9c195c9a..107e643e492 100644
+--- source4/dsdb/samdb/ldb_modules/samldb.c
++++ source4/dsdb/samdb/ldb_modules/samldb.c
+@@ -1103,14 +1103,11 @@ static int samldb_rodc_add(struct samldb_ctx *ac)
+ return LDB_ERR_OTHER;
+
+ found:
+- ret = ldb_msg_add_empty(ac->msg, "msDS-SecondaryKrbTgtNumber",
+- LDB_FLAG_INTERNAL_DISABLE_VALIDATION, NULL);
+- if (ret != LDB_SUCCESS) {
+- return ldb_operr(ldb);
+- }
+
+- ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
+- "msDS-SecondaryKrbTgtNumber", krbtgt_number);
++ ldb_msg_remove_attr(ac->msg, "msDS-SecondaryKrbTgtNumber");
++ ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
++ "msDS-SecondaryKrbTgtNumber", krbtgt_number,
++ LDB_FLAG_INTERNAL_DISABLE_VALIDATION);
+ if (ret != LDB_SUCCESS) {
+ return ldb_operr(ldb);
+ }
+@@ -1792,7 +1789,7 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac)
+ struct ldb_context *ldb = ldb_module_get_ctx(ac->module);
+ void *skip_allocate_sids = ldb_get_opaque(ldb,
+ "skip_allocate_sids");
+- struct ldb_message_element *el, *el2;
++ struct ldb_message_element *el;
+ struct dom_sid *sid;
+ int ret;
+
+@@ -1926,23 +1923,17 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac)
+ /* "isCriticalSystemObject" might be set */
+ if (user_account_control &
+ (UF_SERVER_TRUST_ACCOUNT | UF_PARTIAL_SECRETS_ACCOUNT)) {
+- ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
+- "TRUE");
++ ret = ldb_msg_add_string_flags(ac->msg, "isCriticalSystemObject",
++ "TRUE", LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el2 = ldb_msg_find_element(ac->msg,
+- "isCriticalSystemObject");
+- el2->flags = LDB_FLAG_MOD_REPLACE;
+ } else if (user_account_control & UF_WORKSTATION_TRUST_ACCOUNT) {
+- ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
+- "FALSE");
++ ret = ldb_msg_add_string_flags(ac->msg, "isCriticalSystemObject",
++ "FALSE", LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el2 = ldb_msg_find_element(ac->msg,
+- "isCriticalSystemObject");
+- el2->flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ /* Step 1.4: "userAccountControl" -> "primaryGroupID" mapping */
+@@ -2018,14 +2009,13 @@ static int samldb_objectclass_trigger(struct samldb_ctx *ac)
+ ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+- ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
+- "sAMAccountType",
+- account_type);
++ ret = samdb_msg_add_uint_flags(ldb, ac->msg, ac->msg,
++ "sAMAccountType",
++ account_type,
++ LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el2 = ldb_msg_find_element(ac->msg, "sAMAccountType");
+- el2->flags = LDB_FLAG_MOD_REPLACE;
+ }
+ break;
+ }
+@@ -2945,26 +2935,23 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac)
+ }
+
+ if (old_atype != new_atype) {
+- ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
+- "sAMAccountType", new_atype);
++ ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
++ "sAMAccountType", new_atype,
++ LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el = ldb_msg_find_element(ac->msg, "sAMAccountType");
+- el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ /* As per MS-SAMR 3.1.1.8.10 these flags have not to be set */
+ if ((clear_uac & UF_LOCKOUT) && (old_lockoutTime != 0)) {
+ /* "lockoutTime" reset as per MS-SAMR 3.1.1.8.10 */
+ ldb_msg_remove_attr(ac->msg, "lockoutTime");
+- ret = samdb_msg_add_uint64(ldb, ac->msg, ac->msg, "lockoutTime",
+- (NTTIME)0);
++ ret = samdb_msg_append_uint64(ldb, ac->msg, ac->msg, "lockoutTime",
++ (NTTIME)0, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el = ldb_msg_find_element(ac->msg, "lockoutTime");
+- el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ /*
+@@ -2975,14 +2962,12 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac)
+ * creating the attribute.
+ */
+ if (old_is_critical != new_is_critical || old_atype != new_atype) {
+- ret = ldb_msg_add_string(ac->msg, "isCriticalSystemObject",
+- new_is_critical ? "TRUE": "FALSE");
++ ret = ldb_msg_append_string(ac->msg, "isCriticalSystemObject",
++ new_is_critical ? "TRUE": "FALSE",
++ LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el = ldb_msg_find_element(ac->msg,
+- "isCriticalSystemObject");
+- el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ if (!ldb_msg_find_element(ac->msg, "primaryGroupID") &&
+@@ -2995,14 +2980,12 @@ static int samldb_user_account_control_change(struct samldb_ctx *ac)
+ }
+ }
+
+- ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg,
+- "primaryGroupID", new_pgrid);
++ ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg,
++ "primaryGroupID", new_pgrid,
++ LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el = ldb_msg_find_element(ac->msg,
+- "primaryGroupID");
+- el->flags = LDB_FLAG_MOD_REPLACE;
+ }
+
+ /* Propagate eventual "userAccountControl" attribute changes */
+@@ -3205,13 +3188,12 @@ static int samldb_lockout_time(struct samldb_ctx *ac)
+
+ /* lockoutTime == 0 resets badPwdCount */
+ ldb_msg_remove_attr(ac->msg, "badPwdCount");
+- ret = samdb_msg_add_int(ldb, ac->msg, ac->msg,
+- "badPwdCount", 0);
++ ret = samdb_msg_append_int(ldb, ac->msg, ac->msg,
++ "badPwdCount", 0,
++ LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el = ldb_msg_find_element(ac->msg, "badPwdCount");
+- el->flags = LDB_FLAG_MOD_REPLACE;
+
+ return LDB_SUCCESS;
+ }
+@@ -3309,13 +3291,11 @@ static int samldb_group_type_change(struct samldb_ctx *ac)
+ ldb_set_errstring(ldb, "samldb: Unrecognized account type!");
+ return LDB_ERR_UNWILLING_TO_PERFORM;
+ }
+- ret = samdb_msg_add_uint(ldb, ac->msg, ac->msg, "sAMAccountType",
+- account_type);
++ ret = samdb_msg_append_uint(ldb, ac->msg, ac->msg, "sAMAccountType",
++ account_type, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el = ldb_msg_find_element(ac->msg, "sAMAccountType");
+- el->flags = LDB_FLAG_MOD_REPLACE;
+
+ return LDB_SUCCESS;
+ }
+diff --git source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c
+index 5f8911c66be..99c5955e9e7 100644
+--- source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c
++++ source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c
+@@ -294,14 +294,13 @@ static int tr_prepare_attributes(struct tr_context *ac)
+ return ldb_error(ldb, LDB_ERR_UNWILLING_TO_PERFORM,
+ "reanimate: Unrecognized account type!");
+ }
+- ret = samdb_msg_add_uint(ldb, ac->mod_msg, ac->mod_msg,
+- "sAMAccountType", account_type);
++ ret = samdb_msg_append_uint(ldb, ac->mod_msg, ac->mod_msg,
++ "sAMAccountType", account_type,
++ LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return ldb_error(ldb, LDB_ERR_OPERATIONS_ERROR,
+ "reanimate: Failed to add sAMAccountType to restored object.");
+ }
+- el = ldb_msg_find_element(ac->mod_msg, "sAMAccountType");
+- el->flags = LDB_FLAG_MOD_REPLACE;
+
+ /* Default values set by Windows */
+ ret = samdb_find_or_add_attribute(ldb, ac->mod_msg,
+@@ -324,12 +323,11 @@ static int tr_prepare_attributes(struct tr_context *ac)
+ return ret;
+ }
+
+- ret = ldb_msg_add_string(ac->mod_msg, "objectCategory", value);
++ ret = ldb_msg_append_string(ac->mod_msg, "objectCategory", value,
++ LDB_FLAG_MOD_ADD);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+- el = ldb_msg_find_element(ac->mod_msg, "objectCategory");
+- el->flags = LDB_FLAG_MOD_ADD;
+ }
+
+ return LDB_SUCCESS;
+diff --git source4/nbt_server/wins/winsdb.c source4/nbt_server/wins/winsdb.c
+index e4a7c2042ed..2a05e96bca4 100644
+--- source4/nbt_server/wins/winsdb.c
++++ source4/nbt_server/wins/winsdb.c
+@@ -102,13 +102,11 @@ uint64_t winsdb_set_maxVersion(struct winsdb_handle *h, uint64_t newMaxVersion)
+ msg->dn = dn;
+
+
+- ret = ldb_msg_add_empty(msg, "objectClass", LDB_FLAG_MOD_REPLACE, NULL);
++ ret = ldb_msg_append_string(msg, "objectClass", "winsMaxVersion",
++ LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) goto failed;
+- ret = ldb_msg_add_string(msg, "objectClass", "winsMaxVersion");
+- if (ret != LDB_SUCCESS) goto failed;
+- ret = ldb_msg_add_empty(msg, "maxVersion", LDB_FLAG_MOD_REPLACE, NULL);
+- if (ret != LDB_SUCCESS) goto failed;
+- ret = ldb_msg_add_fmt(msg, "maxVersion", "%llu", (long long)newMaxVersion);
++ ret = ldb_msg_append_fmt(msg, LDB_FLAG_MOD_REPLACE,
++ "maxVersion", "%llu", (long long)newMaxVersion);
+ if (ret != LDB_SUCCESS) goto failed;
+
+ ret = ldb_modify(wins_db, msg);
+@@ -779,8 +777,7 @@ static struct ldb_message *winsdb_message(struct ldb_context *ldb,
+ ret |= ldb_msg_add_winsdb_addr(msg, rec, "address", rec->addresses[i]);
+ }
+ if (rec->registered_by) {
+- ret |= ldb_msg_add_empty(msg, "registeredBy", 0, NULL);
+- ret |= ldb_msg_add_string(msg, "registeredBy", rec->registered_by);
++ ret |= ldb_msg_append_string(msg, "registeredBy", rec->registered_by, 0);
+ }
+ if (ret != LDB_SUCCESS) goto failed;
+ return msg;
+diff --git source4/rpc_server/lsa/dcesrv_lsa.c source4/rpc_server/lsa/dcesrv_lsa.c
+index 15b068aec62..a165ab2b9d6 100644
+--- source4/rpc_server/lsa/dcesrv_lsa.c
++++ source4/rpc_server/lsa/dcesrv_lsa.c
+@@ -1778,12 +1778,7 @@ static NTSTATUS update_uint32_t_value(TALLOC_CTX *mem_ctx,
+ goto done;
+ }
+
+- ret = ldb_msg_add_empty(dest, attribute, flags, NULL);
+- if (ret != LDB_SUCCESS) {
+- return NT_STATUS_NO_MEMORY;
+- }
+-
+- ret = samdb_msg_add_uint(sam_ldb, dest, dest, attribute, value);
++ ret = samdb_msg_append_uint(sam_ldb, dest, dest, attribute, value, flags);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+@@ -1874,13 +1869,7 @@ static NTSTATUS update_trust_user(TALLOC_CTX *mem_ctx,
+ continue;
+ }
+
+- ret = ldb_msg_add_empty(msg, attribute,
+- LDB_FLAG_MOD_REPLACE, NULL);
+- if (ret != LDB_SUCCESS) {
+- return NT_STATUS_NO_MEMORY;
+- }
+-
+- ret = ldb_msg_add_value(msg, attribute, &v, NULL);
++ ret = ldb_msg_append_value(msg, attribute, &v, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+@@ -2166,28 +2155,30 @@ static NTSTATUS setInfoTrustedDomain_base(struct dcesrv_call_state *dce_call,
+ }
+
+ if (add_incoming || del_incoming) {
+- ret = ldb_msg_add_empty(msg, "trustAuthIncoming",
+- LDB_FLAG_MOD_REPLACE, NULL);
+- if (ret != LDB_SUCCESS) {
+- return NT_STATUS_NO_MEMORY;
+- }
+ if (add_incoming) {
+- ret = ldb_msg_add_value(msg, "trustAuthIncoming",
+- &trustAuthIncoming, NULL);
++ ret = ldb_msg_append_value(msg, "trustAuthIncoming",
++ &trustAuthIncoming, LDB_FLAG_MOD_REPLACE);
++ if (ret != LDB_SUCCESS) {
++ return NT_STATUS_NO_MEMORY;
++ }
++ } else {
++ ret = ldb_msg_add_empty(msg, "trustAuthIncoming",
++ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ if (add_outgoing || del_outgoing) {
+- ret = ldb_msg_add_empty(msg, "trustAuthOutgoing",
+- LDB_FLAG_MOD_REPLACE, NULL);
+- if (ret != LDB_SUCCESS) {
+- return NT_STATUS_NO_MEMORY;
+- }
+ if (add_outgoing) {
+- ret = ldb_msg_add_value(msg, "trustAuthOutgoing",
+- &trustAuthOutgoing, NULL);
++ ret = ldb_msg_append_value(msg, "trustAuthOutgoing",
++ &trustAuthOutgoing, LDB_FLAG_MOD_REPLACE);
++ if (ret != LDB_SUCCESS) {
++ return NT_STATUS_NO_MEMORY;
++ }
++ } else {
++ ret = ldb_msg_add_empty(msg, "trustAuthOutgoing",
++ LDB_FLAG_MOD_REPLACE, NULL);
+ if (ret != LDB_SUCCESS) {
+ return NT_STATUS_NO_MEMORY;
+ }
+@@ -4635,14 +4626,8 @@ static NTSTATUS dcesrv_lsa_lsaRSetForestTrustInformation(struct dcesrv_call_stat
+ goto done;
+ }
+
+- ret = ldb_msg_add_empty(msg, "msDS-TrustForestTrustInfo",
+- LDB_FLAG_MOD_REPLACE, NULL);
+- if (ret != LDB_SUCCESS) {
+- status = NT_STATUS_NO_MEMORY;
+- goto done;
+- }
+- ret = ldb_msg_add_value(msg, "msDS-TrustForestTrustInfo",
+- &ft_blob, NULL);
++ ret = ldb_msg_append_value(msg, "msDS-TrustForestTrustInfo",
++ &ft_blob, LDB_FLAG_MOD_REPLACE);
+ if (ret != LDB_SUCCESS) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+diff --git source4/winbind/idmap.c source4/winbind/idmap.c
+index c4039be473a..c6375f8357a 100644
+--- source4/winbind/idmap.c
++++ source4/winbind/idmap.c
+@@ -672,14 +672,8 @@ static NTSTATUS idmap_sid_to_xid(struct idmap_context *idmap_ctx,
+ vals[1].data = (uint8_t *)hwm_string;
+ vals[1].length = strlen(hwm_string);
+ } else {
+- ret = ldb_msg_add_empty(hwm_msg, "xidNumber", LDB_FLAG_MOD_ADD,
+- NULL);
+- if (ret != LDB_SUCCESS) {
+- status = NT_STATUS_NONE_MAPPED;
+- goto failed;
+- }
+-
+- ret = ldb_msg_add_string(hwm_msg, "xidNumber", hwm_string);
++ ret = ldb_msg_append_string(hwm_msg, "xidNumber", hwm_string,
++ LDB_FLAG_MOD_ADD);
+ if (ret != LDB_SUCCESS)
+ {
+ status = NT_STATUS_NONE_MAPPED;
+--
+2.25.1
+
+
+From 7270b68386692829f97d5c51c50108db395b263e Mon Sep 17 00:00:00 2001
+From: Andrew Bartlett <abartlet@samba.org>
+Date: Tue, 14 Jun 2022 15:43:26 +1200
+Subject: [PATCH 14/99] CVE-2022-32746 ldb: Release LDB 2.3.4
+
+* CVE-2022-32746 Use-after-free occurring in database audit logging module (bug 15009)
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15009
+
+Signed-off-by: Andrew Bartlett <abartlet@samba.org>
+---
+ lib/ldb/ABI/ldb-2.3.4.sigs | 291 ++++++++++++++++++++++++++++++
+ lib/ldb/ABI/pyldb-util-2.3.4.sigs | 3 +
+ lib/ldb/wscript | 2 +-
+ 3 files changed, 295 insertions(+), 1 deletion(-)
+ create mode 100644 lib/ldb/ABI/ldb-2.3.4.sigs
+ create mode 100644 lib/ldb/ABI/pyldb-util-2.3.4.sigs
+
+diff --git lib/ldb/ABI/ldb-2.3.4.sigs lib/ldb/ABI/ldb-2.3.4.sigs
+new file mode 100644
+index 00000000000..40388d9e330
+--- /dev/null
++++ lib/ldb/ABI/ldb-2.3.4.sigs
+@@ -0,0 +1,291 @@
++ldb_add: int (struct ldb_context *, const struct ldb_message *)
++ldb_any_comparison: int (struct ldb_context *, void *, ldb_attr_handler_t, const struct ldb_val *, const struct ldb_val *)
++ldb_asprintf_errstring: void (struct ldb_context *, const char *, ...)
++ldb_attr_casefold: char *(TALLOC_CTX *, const char *)
++ldb_attr_dn: int (const char *)
++ldb_attr_in_list: int (const char * const *, const char *)
++ldb_attr_list_copy: const char **(TALLOC_CTX *, const char * const *)
++ldb_attr_list_copy_add: const char **(TALLOC_CTX *, const char * const *, const char *)
++ldb_base64_decode: int (char *)
++ldb_base64_encode: char *(TALLOC_CTX *, const char *, int)
++ldb_binary_decode: struct ldb_val (TALLOC_CTX *, const char *)
++ldb_binary_encode: char *(TALLOC_CTX *, struct ldb_val)
++ldb_binary_encode_string: char *(TALLOC_CTX *, const char *)
++ldb_build_add_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
++ldb_build_del_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
++ldb_build_extended_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const char *, void *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
++ldb_build_mod_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
++ldb_build_rename_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, struct ldb_dn *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
++ldb_build_search_req: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, const char *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
++ldb_build_search_req_ex: int (struct ldb_request **, struct ldb_context *, TALLOC_CTX *, struct ldb_dn *, enum ldb_scope, struct ldb_parse_tree *, const char * const *, struct ldb_control **, void *, ldb_request_callback_t, struct ldb_request *)
++ldb_casefold: char *(struct ldb_context *, TALLOC_CTX *, const char *, size_t)
++ldb_casefold_default: char *(void *, TALLOC_CTX *, const char *, size_t)
++ldb_check_critical_controls: int (struct ldb_control **)
++ldb_comparison_binary: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *)
++ldb_comparison_fold: int (struct ldb_context *, void *, const struct ldb_val *, const struct ldb_val *)
++ldb_connect: int (struct ldb_context *, const char *, unsigned int, const char **)
++ldb_control_to_string: char *(TALLOC_CTX *, const struct ldb_control *)
++ldb_controls_except_specified: struct ldb_control **(struct ldb_control **, TALLOC_CTX *, struct ldb_control *)
++ldb_debug: void (struct ldb_context *, enum ldb_debug_level, const char *, ...)
++ldb_debug_add: void (struct ldb_context *, const char *, ...)
++ldb_debug_end: void (struct ldb_context *, enum ldb_debug_level)
++ldb_debug_set: void (struct ldb_context *, enum ldb_debug_level, const char *, ...)
++ldb_delete: int (struct ldb_context *, struct ldb_dn *)
++ldb_dn_add_base: bool (struct ldb_dn *, struct ldb_dn *)
++ldb_dn_add_base_fmt: bool (struct ldb_dn *, const char *, ...)
++ldb_dn_add_child: bool (struct ldb_dn *, struct ldb_dn *)
++ldb_dn_add_child_fmt: bool (struct ldb_dn *, const char *, ...)
++ldb_dn_add_child_val: bool (struct ldb_dn *, const char *, struct ldb_val)
++ldb_dn_alloc_casefold: char *(TALLOC_CTX *, struct ldb_dn *)
++ldb_dn_alloc_linearized: char *(TALLOC_CTX *, struct ldb_dn *)
++ldb_dn_canonical_ex_string: char *(TALLOC_CTX *, struct ldb_dn *)
++ldb_dn_canonical_string: char *(TALLOC_CTX *, struct ldb_dn *)
++ldb_dn_check_local: bool (struct ldb_module *, struct ldb_dn *)
++ldb_dn_check_special: bool (struct ldb_dn *, const char *)
++ldb_dn_compare: int (struct ldb_dn *, struct ldb_dn *)
++ldb_dn_compare_base: int (struct ldb_dn *, struct ldb_dn *)
++ldb_dn_copy: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *)
++ldb_dn_escape_value: char *(TALLOC_CTX *, struct ldb_val)
++ldb_dn_extended_add_syntax: int (struct ldb_context *, unsigned int, const struct ldb_dn_extended_syntax *)
++ldb_dn_extended_filter: void (struct ldb_dn *, const char * const *)
++ldb_dn_extended_syntax_by_name: const struct ldb_dn_extended_syntax *(struct ldb_context *, const char *)
++ldb_dn_from_ldb_val: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const struct ldb_val *)
++ldb_dn_get_casefold: const char *(struct ldb_dn *)
++ldb_dn_get_comp_num: int (struct ldb_dn *)
++ldb_dn_get_component_name: const char *(struct ldb_dn *, unsigned int)
++ldb_dn_get_component_val: const struct ldb_val *(struct ldb_dn *, unsigned int)
++ldb_dn_get_extended_comp_num: int (struct ldb_dn *)
++ldb_dn_get_extended_component: const struct ldb_val *(struct ldb_dn *, const char *)
++ldb_dn_get_extended_linearized: char *(TALLOC_CTX *, struct ldb_dn *, int)
++ldb_dn_get_ldb_context: struct ldb_context *(struct ldb_dn *)
++ldb_dn_get_linearized: const char *(struct ldb_dn *)
++ldb_dn_get_parent: struct ldb_dn *(TALLOC_CTX *, struct ldb_dn *)
++ldb_dn_get_rdn_name: const char *(struct ldb_dn *)
++ldb_dn_get_rdn_val: const struct ldb_val *(struct ldb_dn *)
++ldb_dn_has_extended: bool (struct ldb_dn *)
++ldb_dn_is_null: bool (struct ldb_dn *)
++ldb_dn_is_special: bool (struct ldb_dn *)
++ldb_dn_is_valid: bool (struct ldb_dn *)
++ldb_dn_map_local: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *)
++ldb_dn_map_rebase_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *)
++ldb_dn_map_remote: struct ldb_dn *(struct ldb_module *, void *, struct ldb_dn *)
++ldb_dn_minimise: bool (struct ldb_dn *)
++ldb_dn_new: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *)
++ldb_dn_new_fmt: struct ldb_dn *(TALLOC_CTX *, struct ldb_context *, const char *, ...)
++ldb_dn_remove_base_components: bool (struct ldb_dn *, unsigned int)
++ldb_dn_remove_child_components: bool (struct ldb_dn *, unsigned int)
++ldb_dn_remove_extended_components: void (struct ldb_dn *)
++ldb_dn_replace_components: bool (struct ldb_dn *, struct ldb_dn *)
++ldb_dn_set_component: int (struct ldb_dn *, int, const char *, const struct ldb_val)
++ldb_dn_set_extended_component: int (struct ldb_dn *, const char *, const struct ldb_val *)
++ldb_dn_update_components: int (struct ldb_dn *, const struct ldb_dn *)
++ldb_dn_validate: bool (struct ldb_dn *)
++ldb_dump_results: void (struct ldb_context *, struct ldb_result *, FILE *)
++ldb_error_at: int (struct ldb_context *, int, const char *, const char *, int)
++ldb_errstring: const char *(struct ldb_context *)
++ldb_extended: int (struct ldb_context *, const char *, void *, struct ldb_result **)
++ldb_extended_default_callback: int (struct ldb_request *, struct ldb_reply *)
++ldb_filter_attrs: int (struct ldb_context *, const struct ldb_message *, const char * const *, struct ldb_message *)
++ldb_filter_from_tree: char *(TALLOC_CTX *, const struct ldb_parse_tree *)
++ldb_get_config_basedn: struct ldb_dn *(struct ldb_context *)
++ldb_get_create_perms: unsigned int (struct ldb_context *)
++ldb_get_default_basedn: struct ldb_dn *(struct ldb_context *)
++ldb_get_event_context: struct tevent_context *(struct ldb_context *)
++ldb_get_flags: unsigned int (struct ldb_context *)
++ldb_get_opaque: void *(struct ldb_context *, const char *)
++ldb_get_root_basedn: struct ldb_dn *(struct ldb_context *)
++ldb_get_schema_basedn: struct ldb_dn *(struct ldb_context *)
++ldb_global_init: int (void)
++ldb_handle_get_event_context: struct tevent_context *(struct ldb_handle *)
++ldb_handle_new: struct ldb_handle *(TALLOC_CTX *, struct ldb_context *)
++ldb_handle_use_global_event_context: void (struct ldb_handle *)
++ldb_handler_copy: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *)
++ldb_handler_fold: int (struct ldb_context *, void *, const struct ldb_val *, struct ldb_val *)
++ldb_init: struct ldb_context *(TALLOC_CTX *, struct tevent_context *)
++ldb_ldif_message_redacted_string: char *(struct ldb_context *, TALLOC_CTX *, enum ldb_changetype, const struct ldb_message *)
++ldb_ldif_message_string: char *(struct ldb_context *, TALLOC_CTX *, enum ldb_changetype, const struct ldb_message *)
++ldb_ldif_parse_modrdn: int (struct ldb_context *, const struct ldb_ldif *, TALLOC_CTX *, struct ldb_dn **, struct ldb_dn **, bool *, struct ldb_dn **, struct ldb_dn **)
++ldb_ldif_read: struct ldb_ldif *(struct ldb_context *, int (*)(void *), void *)
++ldb_ldif_read_file: struct ldb_ldif *(struct ldb_context *, FILE *)
++ldb_ldif_read_file_state: struct ldb_ldif *(struct ldb_context *, struct ldif_read_file_state *)
++ldb_ldif_read_free: void (struct ldb_context *, struct ldb_ldif *)
++ldb_ldif_read_string: struct ldb_ldif *(struct ldb_context *, const char **)
++ldb_ldif_write: int (struct ldb_context *, int (*)(void *, const char *, ...), void *, const struct ldb_ldif *)
++ldb_ldif_write_file: int (struct ldb_context *, FILE *, const struct ldb_ldif *)
++ldb_ldif_write_redacted_trace_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *)
++ldb_ldif_write_string: char *(struct ldb_context *, TALLOC_CTX *, const struct ldb_ldif *)
++ldb_load_modules: int (struct ldb_context *, const char **)
++ldb_map_add: int (struct ldb_module *, struct ldb_request *)
++ldb_map_delete: int (struct ldb_module *, struct ldb_request *)
++ldb_map_init: int (struct ldb_module *, const struct ldb_map_attribute *, const struct ldb_map_objectclass *, const char * const *, const char *, const char *)
++ldb_map_modify: int (struct ldb_module *, struct ldb_request *)
++ldb_map_rename: int (struct ldb_module *, struct ldb_request *)
++ldb_map_search: int (struct ldb_module *, struct ldb_request *)
++ldb_match_message: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, enum ldb_scope, bool *)
++ldb_match_msg: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope)
++ldb_match_msg_error: int (struct ldb_context *, const struct ldb_message *, const struct ldb_parse_tree *, struct ldb_dn *, enum ldb_scope, bool *)
++ldb_match_msg_objectclass: int (const struct ldb_message *, const char *)
++ldb_mod_register_control: int (struct ldb_module *, const char *)
++ldb_modify: int (struct ldb_context *, const struct ldb_message *)
++ldb_modify_default_callback: int (struct ldb_request *, struct ldb_reply *)
++ldb_module_call_chain: char *(struct ldb_request *, TALLOC_CTX *)
++ldb_module_connect_backend: int (struct ldb_context *, const char *, const char **, struct ldb_module **)
++ldb_module_done: int (struct ldb_request *, struct ldb_control **, struct ldb_extended *, int)
++ldb_module_flags: uint32_t (struct ldb_context *)
++ldb_module_get_ctx: struct ldb_context *(struct ldb_module *)
++ldb_module_get_name: const char *(struct ldb_module *)
++ldb_module_get_ops: const struct ldb_module_ops *(struct ldb_module *)
++ldb_module_get_private: void *(struct ldb_module *)
++ldb_module_init_chain: int (struct ldb_context *, struct ldb_module *)
++ldb_module_load_list: int (struct ldb_context *, const char **, struct ldb_module *, struct ldb_module **)
++ldb_module_new: struct ldb_module *(TALLOC_CTX *, struct ldb_context *, const char *, const struct ldb_module_ops *)
++ldb_module_next: struct ldb_module *(struct ldb_module *)
++ldb_module_popt_options: struct poptOption **(struct ldb_context *)
++ldb_module_send_entry: int (struct ldb_request *, struct ldb_message *, struct ldb_control **)
++ldb_module_send_referral: int (struct ldb_request *, char *)
++ldb_module_set_next: void (struct ldb_module *, struct ldb_module *)
++ldb_module_set_private: void (struct ldb_module *, void *)
++ldb_modules_hook: int (struct ldb_context *, enum ldb_module_hook_type)
++ldb_modules_list_from_string: const char **(struct ldb_context *, TALLOC_CTX *, const char *)
++ldb_modules_load: int (const char *, const char *)
++ldb_msg_add: int (struct ldb_message *, const struct ldb_message_element *, int)
++ldb_msg_add_empty: int (struct ldb_message *, const char *, int, struct ldb_message_element **)
++ldb_msg_add_fmt: int (struct ldb_message *, const char *, const char *, ...)
++ldb_msg_add_linearized_dn: int (struct ldb_message *, const char *, struct ldb_dn *)
++ldb_msg_add_steal_string: int (struct ldb_message *, const char *, char *)
++ldb_msg_add_steal_value: int (struct ldb_message *, const char *, struct ldb_val *)
++ldb_msg_add_string: int (struct ldb_message *, const char *, const char *)
++ldb_msg_add_string_flags: int (struct ldb_message *, const char *, const char *, int)
++ldb_msg_add_value: int (struct ldb_message *, const char *, const struct ldb_val *, struct ldb_message_element **)
++ldb_msg_append_fmt: int (struct ldb_message *, int, const char *, const char *, ...)
++ldb_msg_append_linearized_dn: int (struct ldb_message *, const char *, struct ldb_dn *, int)
++ldb_msg_append_steal_string: int (struct ldb_message *, const char *, char *, int)
++ldb_msg_append_steal_value: int (struct ldb_message *, const char *, struct ldb_val *, int)
++ldb_msg_append_string: int (struct ldb_message *, const char *, const char *, int)
++ldb_msg_append_value: int (struct ldb_message *, const char *, const struct ldb_val *, int)
++ldb_msg_canonicalize: struct ldb_message *(struct ldb_context *, const struct ldb_message *)
++ldb_msg_check_string_attribute: int (const struct ldb_message *, const char *, const char *)
++ldb_msg_copy: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *)
++ldb_msg_copy_attr: int (struct ldb_message *, const char *, const char *)
++ldb_msg_copy_shallow: struct ldb_message *(TALLOC_CTX *, const struct ldb_message *)
++ldb_msg_diff: struct ldb_message *(struct ldb_context *, struct ldb_message *, struct ldb_message *)
++ldb_msg_difference: int (struct ldb_context *, TALLOC_CTX *, struct ldb_message *, struct ldb_message *, struct ldb_message **)
++ldb_msg_element_add_value: int (TALLOC_CTX *, struct ldb_message_element *, const struct ldb_val *)
++ldb_msg_element_compare: int (struct ldb_message_element *, struct ldb_message_element *)
++ldb_msg_element_compare_name: int (struct ldb_message_element *, struct ldb_message_element *)
++ldb_msg_element_equal_ordered: bool (const struct ldb_message_element *, const struct ldb_message_element *)
++ldb_msg_find_attr_as_bool: int (const struct ldb_message *, const char *, int)
++ldb_msg_find_attr_as_dn: struct ldb_dn *(struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, const char *)
++ldb_msg_find_attr_as_double: double (const struct ldb_message *, const char *, double)
++ldb_msg_find_attr_as_int: int (const struct ldb_message *, const char *, int)
++ldb_msg_find_attr_as_int64: int64_t (const struct ldb_message *, const char *, int64_t)
++ldb_msg_find_attr_as_string: const char *(const struct ldb_message *, const char *, const char *)
++ldb_msg_find_attr_as_uint: unsigned int (const struct ldb_message *, const char *, unsigned int)
++ldb_msg_find_attr_as_uint64: uint64_t (const struct ldb_message *, const char *, uint64_t)
++ldb_msg_find_common_values: int (struct ldb_context *, TALLOC_CTX *, struct ldb_message_element *, struct ldb_message_element *, uint32_t)
++ldb_msg_find_duplicate_val: int (struct ldb_context *, TALLOC_CTX *, const struct ldb_message_element *, struct ldb_val **, uint32_t)
++ldb_msg_find_element: struct ldb_message_element *(const struct ldb_message *, const char *)
++ldb_msg_find_ldb_val: const struct ldb_val *(const struct ldb_message *, const char *)
++ldb_msg_find_val: struct ldb_val *(const struct ldb_message_element *, struct ldb_val *)
++ldb_msg_new: struct ldb_message *(TALLOC_CTX *)
++ldb_msg_normalize: int (struct ldb_context *, TALLOC_CTX *, const struct ldb_message *, struct ldb_message **)
++ldb_msg_remove_attr: void (struct ldb_message *, const char *)
++ldb_msg_remove_element: void (struct ldb_message *, struct ldb_message_element *)
++ldb_msg_rename_attr: int (struct ldb_message *, const char *, const char *)
++ldb_msg_sanity_check: int (struct ldb_context *, const struct ldb_message *)
++ldb_msg_sort_elements: void (struct ldb_message *)
++ldb_next_del_trans: int (struct ldb_module *)
++ldb_next_end_trans: int (struct ldb_module *)
++ldb_next_init: int (struct ldb_module *)
++ldb_next_prepare_commit: int (struct ldb_module *)
++ldb_next_read_lock: int (struct ldb_module *)
++ldb_next_read_unlock: int (struct ldb_module *)
++ldb_next_remote_request: int (struct ldb_module *, struct ldb_request *)
++ldb_next_request: int (struct ldb_module *, struct ldb_request *)
++ldb_next_start_trans: int (struct ldb_module *)
++ldb_op_default_callback: int (struct ldb_request *, struct ldb_reply *)
++ldb_options_copy: const char **(TALLOC_CTX *, const char **)
++ldb_options_find: const char *(struct ldb_context *, const char **, const char *)
++ldb_options_get: const char **(struct ldb_context *)
++ldb_pack_data: int (struct ldb_context *, const struct ldb_message *, struct ldb_val *, uint32_t)
++ldb_parse_control_from_string: struct ldb_control *(struct ldb_context *, TALLOC_CTX *, const char *)
++ldb_parse_control_strings: struct ldb_control **(struct ldb_context *, TALLOC_CTX *, const char **)
++ldb_parse_tree: struct ldb_parse_tree *(TALLOC_CTX *, const char *)
++ldb_parse_tree_attr_replace: void (struct ldb_parse_tree *, const char *, const char *)
++ldb_parse_tree_copy_shallow: struct ldb_parse_tree *(TALLOC_CTX *, const struct ldb_parse_tree *)
++ldb_parse_tree_walk: int (struct ldb_parse_tree *, int (*)(struct ldb_parse_tree *, void *), void *)
++ldb_qsort: void (void * const, size_t, size_t, void *, ldb_qsort_cmp_fn_t)
++ldb_register_backend: int (const char *, ldb_connect_fn, bool)
++ldb_register_extended_match_rule: int (struct ldb_context *, const struct ldb_extended_match_rule *)
++ldb_register_hook: int (ldb_hook_fn)
++ldb_register_module: int (const struct ldb_module_ops *)
++ldb_rename: int (struct ldb_context *, struct ldb_dn *, struct ldb_dn *)
++ldb_reply_add_control: int (struct ldb_reply *, const char *, bool, void *)
++ldb_reply_get_control: struct ldb_control *(struct ldb_reply *, const char *)
++ldb_req_get_custom_flags: uint32_t (struct ldb_request *)
++ldb_req_is_untrusted: bool (struct ldb_request *)
++ldb_req_location: const char *(struct ldb_request *)
++ldb_req_mark_trusted: void (struct ldb_request *)
++ldb_req_mark_untrusted: void (struct ldb_request *)
++ldb_req_set_custom_flags: void (struct ldb_request *, uint32_t)
++ldb_req_set_location: void (struct ldb_request *, const char *)
++ldb_request: int (struct ldb_context *, struct ldb_request *)
++ldb_request_add_control: int (struct ldb_request *, const char *, bool, void *)
++ldb_request_done: int (struct ldb_request *, int)
++ldb_request_get_control: struct ldb_control *(struct ldb_request *, const char *)
++ldb_request_get_status: int (struct ldb_request *)
++ldb_request_replace_control: int (struct ldb_request *, const char *, bool, void *)
++ldb_request_set_state: void (struct ldb_request *, int)
++ldb_reset_err_string: void (struct ldb_context *)
++ldb_save_controls: int (struct ldb_control *, struct ldb_request *, struct ldb_control ***)
++ldb_schema_attribute_add: int (struct ldb_context *, const char *, unsigned int, const char *)
++ldb_schema_attribute_add_with_syntax: int (struct ldb_context *, const char *, unsigned int, const struct ldb_schema_syntax *)
++ldb_schema_attribute_by_name: const struct ldb_schema_attribute *(struct ldb_context *, const char *)
++ldb_schema_attribute_fill_with_syntax: int (struct ldb_context *, TALLOC_CTX *, const char *, unsigned int, const struct ldb_schema_syntax *, struct ldb_schema_attribute *)
++ldb_schema_attribute_remove: void (struct ldb_context *, const char *)
++ldb_schema_attribute_remove_flagged: void (struct ldb_context *, unsigned int)
++ldb_schema_attribute_set_override_handler: void (struct ldb_context *, ldb_attribute_handler_override_fn_t, void *)
++ldb_schema_set_override_GUID_index: void (struct ldb_context *, const char *, const char *)
++ldb_schema_set_override_indexlist: void (struct ldb_context *, bool)
++ldb_search: int (struct ldb_context *, TALLOC_CTX *, struct ldb_result **, struct ldb_dn *, enum ldb_scope, const char * const *, const char *, ...)
++ldb_search_default_callback: int (struct ldb_request *, struct ldb_reply *)
++ldb_sequence_number: int (struct ldb_context *, enum ldb_sequence_type, uint64_t *)
++ldb_set_create_perms: void (struct ldb_context *, unsigned int)
++ldb_set_debug: int (struct ldb_context *, void (*)(void *, enum ldb_debug_level, const char *, va_list), void *)
++ldb_set_debug_stderr: int (struct ldb_context *)
++ldb_set_default_dns: void (struct ldb_context *)
++ldb_set_errstring: void (struct ldb_context *, const char *)
++ldb_set_event_context: void (struct ldb_context *, struct tevent_context *)
++ldb_set_flags: void (struct ldb_context *, unsigned int)
++ldb_set_modules_dir: void (struct ldb_context *, const char *)
++ldb_set_opaque: int (struct ldb_context *, const char *, void *)
++ldb_set_require_private_event_context: void (struct ldb_context *)
++ldb_set_timeout: int (struct ldb_context *, struct ldb_request *, int)
++ldb_set_timeout_from_prev_req: int (struct ldb_context *, struct ldb_request *, struct ldb_request *)
++ldb_set_utf8_default: void (struct ldb_context *)
++ldb_set_utf8_fns: void (struct ldb_context *, void *, char *(*)(void *, void *, const char *, size_t))
++ldb_setup_wellknown_attributes: int (struct ldb_context *)
++ldb_should_b64_encode: int (struct ldb_context *, const struct ldb_val *)
++ldb_standard_syntax_by_name: const struct ldb_schema_syntax *(struct ldb_context *, const char *)
++ldb_strerror: const char *(int)
++ldb_string_to_time: time_t (const char *)
++ldb_string_utc_to_time: time_t (const char *)
++ldb_timestring: char *(TALLOC_CTX *, time_t)
++ldb_timestring_utc: char *(TALLOC_CTX *, time_t)
++ldb_transaction_cancel: int (struct ldb_context *)
++ldb_transaction_cancel_noerr: int (struct ldb_context *)
++ldb_transaction_commit: int (struct ldb_context *)
++ldb_transaction_prepare_commit: int (struct ldb_context *)
++ldb_transaction_start: int (struct ldb_context *)
++ldb_unpack_data: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *)
++ldb_unpack_data_flags: int (struct ldb_context *, const struct ldb_val *, struct ldb_message *, unsigned int)
++ldb_unpack_get_format: int (const struct ldb_val *, uint32_t *)
++ldb_val_dup: struct ldb_val (TALLOC_CTX *, const struct ldb_val *)
++ldb_val_equal_exact: int (const struct ldb_val *, const struct ldb_val *)
++ldb_val_map_local: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *)
++ldb_val_map_remote: struct ldb_val (struct ldb_module *, void *, const struct ldb_map_attribute *, const struct ldb_val *)
++ldb_val_string_cmp: int (const struct ldb_val *, const char *)
++ldb_val_to_time: int (const struct ldb_val *, time_t *)
++ldb_valid_attr_name: int (const char *)
++ldb_vdebug: void (struct ldb_context *, enum ldb_debug_level, const char *, va_list)
++ldb_wait: int (struct ldb_handle *, enum ldb_wait_type)
+diff --git lib/ldb/ABI/pyldb-util-2.3.4.sigs lib/ldb/ABI/pyldb-util-2.3.4.sigs
+new file mode 100644
+index 00000000000..164a806b2ff
+--- /dev/null
++++ lib/ldb/ABI/pyldb-util-2.3.4.sigs
+@@ -0,0 +1,3 @@
++pyldb_Dn_FromDn: PyObject *(struct ldb_dn *)
++pyldb_Object_AsDn: bool (TALLOC_CTX *, PyObject *, struct ldb_context *, struct ldb_dn **)
++pyldb_check_type: bool (PyObject *, const char *)
+
+--
+2.25.1
+
+
+From 6237c85565332e0be1890dd57cc7e25fb76571d7 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 16 Feb 2022 17:03:10 +1300
+Subject: [PATCH 15/99] CVE-2022-32745 s4/dsdb/samldb: Check for empty values
+ array
+
+This avoids potentially trying to access the first element of an empty
+array.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15008
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/samldb.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git source4/dsdb/samdb/ldb_modules/samldb.c source4/dsdb/samdb/ldb_modules/samldb.c
+index 107e643e492..3625bb42e58 100644
+--- source4/dsdb/samdb/ldb_modules/samldb.c
++++ source4/dsdb/samdb/ldb_modules/samldb.c
+@@ -751,7 +751,7 @@ static int samldb_schema_add_handle_linkid(struct samldb_ctx *ac)
+ return ret;
+ }
+
+- if (el == NULL) {
++ if (el == NULL || el->num_values == 0) {
+ return LDB_SUCCESS;
+ }
+
+@@ -919,7 +919,7 @@ static int samldb_schema_add_handle_mapiid(struct samldb_ctx *ac)
+ return ret;
+ }
+
+- if (el == NULL) {
++ if (el == NULL || el->num_values == 0) {
+ return LDB_SUCCESS;
+ }
+
+--
+2.25.1
+
+
+From 7c8427e5d2f247921ab44996829acfed1f5f2360 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 17 Feb 2022 11:11:53 +1300
+Subject: [PATCH 16/99] CVE-2022-32745 s4/dsdb/util: Use correct value for loop
+ count limit
+
+Currently, we can crash the server by sending a large number of values
+of a specific attribute (such as sAMAccountName) spread across a few
+message elements. If val_count is larger than the total number of
+elements, we get an access beyond the elements array.
+
+Similarly, we can include unrelated message elements prior to the
+message elements of the attribute in question, so that not all of the
+attribute's values are copied into the returned elements values array.
+This can cause the server to access uninitialised data, likely resulting
+in a crash or unexpected behaviour.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15008
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/util.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git source4/dsdb/samdb/ldb_modules/util.c source4/dsdb/samdb/ldb_modules/util.c
+index 405febf0b3d..14947746837 100644
+--- source4/dsdb/samdb/ldb_modules/util.c
++++ source4/dsdb/samdb/ldb_modules/util.c
+@@ -1546,7 +1546,7 @@ int dsdb_get_expected_new_values(TALLOC_CTX *mem_ctx,
+
+ v = _el->values;
+
+- for (i = 0; i < val_count; i++) {
++ for (i = 0; i < msg->num_elements; i++) {
+ if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
+ if ((operation == LDB_MODIFY) &&
+ (LDB_FLAG_MOD_TYPE(msg->elements[i].flags)
+--
+2.25.1
+
+
+From 4d2d30c21b16a53d5547cb803efe49cb6304ce37 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 17 Feb 2022 11:13:38 +1300
+Subject: [PATCH 17/99] CVE-2022-32745 s4/dsdb/util: Don't call memcpy() with a
+ NULL pointer
+
+Doing so is undefined behaviour.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15008
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/util.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git source4/dsdb/samdb/ldb_modules/util.c source4/dsdb/samdb/ldb_modules/util.c
+index 14947746837..35ae110b5ef 100644
+--- source4/dsdb/samdb/ldb_modules/util.c
++++ source4/dsdb/samdb/ldb_modules/util.c
+@@ -1548,15 +1548,19 @@ int dsdb_get_expected_new_values(TALLOC_CTX *mem_ctx,
+
+ for (i = 0; i < msg->num_elements; i++) {
+ if (ldb_attr_cmp(msg->elements[i].name, attr_name) == 0) {
++ const struct ldb_message_element *tmp_el = &msg->elements[i];
+ if ((operation == LDB_MODIFY) &&
+- (LDB_FLAG_MOD_TYPE(msg->elements[i].flags)
++ (LDB_FLAG_MOD_TYPE(tmp_el->flags)
+ == LDB_FLAG_MOD_DELETE)) {
+ continue;
+ }
++ if (tmp_el->values == NULL || tmp_el->num_values == 0) {
++ continue;
++ }
+ memcpy(v,
+- msg->elements[i].values,
+- msg->elements[i].num_values);
+- v += msg->elements[i].num_values;
++ tmp_el->values,
++ tmp_el->num_values);
++ v += tmp_el->num_values;
+ }
+ }
+
+--
+2.25.1
+
+
+From 65d96369fa4f915f01e203cfc8b15e48c5b4b440 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Fri, 3 Jun 2022 16:16:31 +1200
+Subject: [PATCH 18/99] CVE-2022-32745 s4/dsdb/util: Correctly copy values into
+ message element
+
+To use memcpy(), we need to specify the number of bytes to copy, rather
+than the number of ldb_val structures.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15008
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/util.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git source4/dsdb/samdb/ldb_modules/util.c source4/dsdb/samdb/ldb_modules/util.c
+index 35ae110b5ef..e7fe8f855df 100644
+--- source4/dsdb/samdb/ldb_modules/util.c
++++ source4/dsdb/samdb/ldb_modules/util.c
+@@ -1559,7 +1559,7 @@ int dsdb_get_expected_new_values(TALLOC_CTX *mem_ctx,
+ }
+ memcpy(v,
+ tmp_el->values,
+- tmp_el->num_values);
++ tmp_el->num_values * sizeof(*v));
+ v += tmp_el->num_values;
+ }
+ }
+--
+2.25.1
+
+
+From 34eb92a2066cc403aac5a3708257b04a40ba19ee Mon Sep 17 00:00:00 2001
+From: Isaac Boukris <iboukris@gmail.com>
+Date: Sat, 19 Sep 2020 14:16:20 +0200
+Subject: [PATCH 19/99] s4:mit-kdb: Force canonicalization for looking up
+ principals
+
+See also
+https://github.com/krb5/krb5/commit/ac8865a22138ab0c657208c41be8fd6bc7968148
+
+Pair-Programmed-With: Andreas Schneider <asn@samba.org>
+Signed-off-by: Isaac Boukris <iboukris@gmail.com>
+Signed-off-by: Andreas Schneider <asn@samba.org>
+Reviewed-by: Alexander Bokovoy <ab@samba.org>
+
+Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
+Autobuild-Date(master): Mon Nov 29 09:32:26 UTC 2021 on sn-devel-184
+
+(cherry picked from commit 90febd2a33b88af49af595fe0e995d6ba0f33a1b)
+
+[jsutton@samba.org Removed MIT knownfail changes]
+---
+ source4/heimdal/lib/hdb/hdb.h | 1 +
+ source4/kdc/db-glue.c | 7 ++++++-
+ source4/kdc/mit_samba.c | 8 ++++++++
+ source4/kdc/sdb.h | 1 +
+ 4 files changed, 16 insertions(+), 1 deletion(-)
+
+diff --git source4/heimdal/lib/hdb/hdb.h source4/heimdal/lib/hdb/hdb.h
+index 5ef9d9565f3..dafaffc6c2d 100644
+--- source4/heimdal/lib/hdb/hdb.h
++++ source4/heimdal/lib/hdb/hdb.h
+@@ -63,6 +63,7 @@ enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK };
+ #define HDB_F_ALL_KVNOS 2048 /* we want all the keys, live or not */
+ #define HDB_F_FOR_AS_REQ 4096 /* fetch is for a AS REQ */
+ #define HDB_F_FOR_TGS_REQ 8192 /* fetch is for a TGS REQ */
++#define HDB_F_FORCE_CANON 16384 /* force canonicalition */
+
+ /* hdb_capability_flags */
+ #define HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL 1
+diff --git source4/kdc/db-glue.c source4/kdc/db-glue.c
+index 3a7e2176653..ac47fe78373 100644
+--- source4/kdc/db-glue.c
++++ source4/kdc/db-glue.c
+@@ -957,11 +957,16 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
+ krb5_clear_error_message(context);
+ goto out;
+ }
+- } else if ((flags & SDB_F_CANON) && (flags & SDB_F_FOR_AS_REQ)) {
++ } else if ((flags & SDB_F_FORCE_CANON) ||
++ ((flags & SDB_F_CANON) && (flags & SDB_F_FOR_AS_REQ))) {
+ /*
+ * SDB_F_CANON maps from the canonicalize flag in the
+ * packet, and has a different meaning between AS-REQ
+ * and TGS-REQ. We only change the principal in the AS-REQ case
++ *
++ * The SDB_F_FORCE_CANON if for new MIT KDC code that wants
++ * the canonical name in all lookups, and takes care to
++ * canonicalize only when appropriate.
+ */
+ ret = smb_krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL);
+ if (ret) {
+diff --git source4/kdc/mit_samba.c source4/kdc/mit_samba.c
+index e015c5a52db..c2a604045d9 100644
+--- source4/kdc/mit_samba.c
++++ source4/kdc/mit_samba.c
+@@ -195,6 +195,14 @@ int mit_samba_get_principal(struct mit_samba_context *ctx,
+ return ENOMEM;
+ }
+
++#if KRB5_KDB_API_VERSION >= 10
++ /*
++ * The MIT KDC code that wants the canonical name in all lookups, and
++ * takes care to canonicalize only when appropriate.
++ */
++ sflags |= SDB_F_FORCE_CANON;
++#endif
++
+ if (kflags & KRB5_KDB_FLAG_CANONICALIZE) {
+ sflags |= SDB_F_CANON;
+ }
+diff --git source4/kdc/sdb.h source4/kdc/sdb.h
+index c929acccce6..a9115ec23d7 100644
+--- source4/kdc/sdb.h
++++ source4/kdc/sdb.h
+@@ -116,6 +116,7 @@ struct sdb_entry_ex {
+ #define SDB_F_KVNO_SPECIFIED 128 /* we want a particular KVNO */
+ #define SDB_F_FOR_AS_REQ 4096 /* fetch is for a AS REQ */
+ #define SDB_F_FOR_TGS_REQ 8192 /* fetch is for a TGS REQ */
++#define SDB_F_FORCE_CANON 16384 /* force canonicalition */
+
+ void sdb_free_entry(struct sdb_entry_ex *e);
+ void free_sdb_entry(struct sdb_entry *s);
+--
+2.25.1
+
+
+From 06a0a75b16bace9c29568653d9e4bde4050c5ee5 Mon Sep 17 00:00:00 2001
+From: Andreas Schneider <asn@samba.org>
+Date: Tue, 21 Dec 2021 12:17:11 +0100
+Subject: [PATCH 20/99] s4:kdc: Also cannoicalize krbtgt principals when
+ enforcing canonicalization
+
+Signed-off-by: Andreas Schneider <asn@samba.org>
+Reviewed-by: Stefan Metzmacher <metze@samba.org>
+(cherry picked from commit f1ec950aeb47283a504018bafa21f54c3282e70c)
+---
+ source4/kdc/db-glue.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git source4/kdc/db-glue.c source4/kdc/db-glue.c
+index ac47fe78373..d017741e30a 100644
+--- source4/kdc/db-glue.c
++++ source4/kdc/db-glue.c
+@@ -920,7 +920,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
+ if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT) {
+ p->is_krbtgt = true;
+
+- if (flags & (SDB_F_CANON)) {
++ if (flags & (SDB_F_CANON|SDB_F_FORCE_CANON)) {
+ /*
+ * When requested to do so, ensure that the
+ * both realm values in the principal are set
+--
+2.25.1
+
+
+From b4005403032b0b33ca88d3abcbf085621b32bd5b Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:30:38 +1300
+Subject: [PATCH 21/99] selftest: Check received LDB error code when
+ STRICT_CHECKING=0
+
+We were instead only checking the expected error.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit ad4d6fb01fd8083e68f07c427af8932574810cdc)
+---
+ source4/dsdb/tests/python/priv_attrs.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git source4/dsdb/tests/python/priv_attrs.py source4/dsdb/tests/python/priv_attrs.py
+index aa35dcc1317..4dfdfb9cbb8 100644
+--- source4/dsdb/tests/python/priv_attrs.py
++++ source4/dsdb/tests/python/priv_attrs.py
+@@ -167,7 +167,7 @@ class PrivAttrsTests(samba.tests.TestCase):
+ creds_tmp.set_kerberos_state(DONT_USE_KERBEROS) # kinit is too expensive to use in a tight loop
+ return creds_tmp
+
+- def assertGotLdbError(self, got, wanted):
++ def assertGotLdbError(self, wanted, got):
+ if not self.strict_checking:
+ self.assertNotEqual(got, ldb.SUCCESS)
+ else:
+--
+2.25.1
+
+
+From 6a4ed078902dcc57ab14f701c88e76ec0ac375e7 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:53:18 +1300
+Subject: [PATCH 22/99] tests/krb5: Remove unused variable
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 57b1b76154d699b9d70ad04fa5e94c4b30f0e4bf)
+---
+ python/samba/tests/krb5/raw_testcase.py | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git python/samba/tests/krb5/raw_testcase.py python/samba/tests/krb5/raw_testcase.py
+index 42f2e94f5aa..36a6134e6c9 100644
+--- python/samba/tests/krb5/raw_testcase.py
++++ python/samba/tests/krb5/raw_testcase.py
+@@ -2855,7 +2855,6 @@ class RawKerberosTest(TestCaseInTempDir):
+
+ expect_etype_info2 = ()
+ expect_etype_info = False
+- unexpect_etype_info = True
+ expected_aes_type = 0
+ expected_rc4_type = 0
+ if kcrypto.Enctype.RC4 in proposed_etypes:
+@@ -2868,7 +2867,6 @@ class RawKerberosTest(TestCaseInTempDir):
+ if etype > expected_aes_type:
+ expected_aes_type = etype
+ if etype in (kcrypto.Enctype.RC4,) and error_code != 0:
+- unexpect_etype_info = False
+ if etype > expected_rc4_type:
+ expected_rc4_type = etype
+
+--
+2.25.1
+
+
+From 837453d34799f44653d0d6d690d3e3d5eb074993 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:34:11 +1300
+Subject: [PATCH 23/99] tests/krb5: Deduplicate AS-REQ tests
+
+salt_tests was running the tests defined in the base class as well as
+its own tests.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit f0b222e3ecf72c8562bc97bedd9f3a92980b60d5)
+---
+ python/samba/tests/krb5/as_req_tests.py | 163 ++++++++++++------------
+ python/samba/tests/krb5/salt_tests.py | 4 +-
+ 2 files changed, 85 insertions(+), 82 deletions(-)
+
+diff --git python/samba/tests/krb5/as_req_tests.py python/samba/tests/krb5/as_req_tests.py
+index 08081928363..315720f85d6 100755
+--- python/samba/tests/krb5/as_req_tests.py
++++ python/samba/tests/krb5/as_req_tests.py
+@@ -38,87 +38,8 @@ from samba.tests.krb5.rfc4120_constants import (
+ global_asn1_print = False
+ global_hexdump = False
+
+-@DynamicTestCase
+-class AsReqKerberosTests(KDCBaseTest):
+-
+- @classmethod
+- def setUpDynamicTestCases(cls):
+- for (name, idx) in cls.etype_test_permutation_name_idx():
+- for pac in [None, True, False]:
+- tname = "%s_pac_%s" % (name, pac)
+- targs = (idx, pac)
+- cls.generate_dynamic_test("test_as_req_no_preauth", tname, *targs)
+-
+- def setUp(self):
+- super(AsReqKerberosTests, self).setUp()
+- self.do_asn1_print = global_asn1_print
+- self.do_hexdump = global_hexdump
+-
+- def _test_as_req_nopreauth(self,
+- initial_etypes,
+- pac=None,
+- initial_kdc_options=None):
+- client_creds = self.get_client_creds()
+- client_account = client_creds.get_username()
+- client_as_etypes = self.get_default_enctypes()
+- krbtgt_creds = self.get_krbtgt_creds(require_keys=False)
+- krbtgt_account = krbtgt_creds.get_username()
+- realm = krbtgt_creds.get_realm()
+-
+- cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
+- names=[client_account])
+- sname = self.PrincipalName_create(name_type=NT_SRV_INST,
+- names=[krbtgt_account, realm])
+-
+- expected_crealm = realm
+- expected_cname = cname
+- expected_srealm = realm
+- expected_sname = sname
+- expected_salt = client_creds.get_salt()
+-
+- if any(etype in client_as_etypes and etype in initial_etypes
+- for etype in (kcrypto.Enctype.AES256,
+- kcrypto.Enctype.AES128,
+- kcrypto.Enctype.RC4)):
+- expected_error_mode = KDC_ERR_PREAUTH_REQUIRED
+- else:
+- expected_error_mode = KDC_ERR_ETYPE_NOSUPP
+-
+- kdc_exchange_dict = self.as_exchange_dict(
+- expected_crealm=expected_crealm,
+- expected_cname=expected_cname,
+- expected_srealm=expected_srealm,
+- expected_sname=expected_sname,
+- generate_padata_fn=None,
+- check_error_fn=self.generic_check_kdc_error,
+- check_rep_fn=None,
+- expected_error_mode=expected_error_mode,
+- client_as_etypes=client_as_etypes,
+- expected_salt=expected_salt,
+- kdc_options=str(initial_kdc_options),
+- pac_request=pac)
+-
+- self._generic_kdc_exchange(kdc_exchange_dict,
+- cname=cname,
+- realm=realm,
+- sname=sname,
+- etypes=initial_etypes)
+-
+- def _test_as_req_no_preauth_with_args(self, etype_idx, pac):
+- name, etypes = self.etype_test_permutation_by_idx(etype_idx)
+- self._test_as_req_nopreauth(
+- pac=pac,
+- initial_etypes=etypes,
+- initial_kdc_options=krb5_asn1.KDCOptions('forwardable'))
+-
+- def test_as_req_enc_timestamp(self):
+- client_creds = self.get_client_creds()
+- self._run_as_req_enc_timestamp(client_creds)
+-
+- def test_as_req_enc_timestamp_mac(self):
+- client_creds = self.get_mach_creds()
+- self._run_as_req_enc_timestamp(client_creds)
+
++class AsReqBaseTest(KDCBaseTest):
+ def _run_as_req_enc_timestamp(self, client_creds):
+ client_account = client_creds.get_username()
+ client_as_etypes = self.get_default_enctypes()
+@@ -207,6 +128,88 @@ class AsReqKerberosTests(KDCBaseTest):
+ return etype_info2
+
+
++@DynamicTestCase
++class AsReqKerberosTests(AsReqBaseTest):
++
++ @classmethod
++ def setUpDynamicTestCases(cls):
++ for (name, idx) in cls.etype_test_permutation_name_idx():
++ for pac in [None, True, False]:
++ tname = "%s_pac_%s" % (name, pac)
++ targs = (idx, pac)
++ cls.generate_dynamic_test("test_as_req_no_preauth", tname, *targs)
++
++ def setUp(self):
++ super(AsReqKerberosTests, self).setUp()
++ self.do_asn1_print = global_asn1_print
++ self.do_hexdump = global_hexdump
++
++ def _test_as_req_nopreauth(self,
++ initial_etypes,
++ pac=None,
++ initial_kdc_options=None):
++ client_creds = self.get_client_creds()
++ client_account = client_creds.get_username()
++ client_as_etypes = self.get_default_enctypes()
++ krbtgt_creds = self.get_krbtgt_creds(require_keys=False)
++ krbtgt_account = krbtgt_creds.get_username()
++ realm = krbtgt_creds.get_realm()
++
++ cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
++ names=[client_account])
++ sname = self.PrincipalName_create(name_type=NT_SRV_INST,
++ names=[krbtgt_account, realm])
++
++ expected_crealm = realm
++ expected_cname = cname
++ expected_srealm = realm
++ expected_sname = sname
++ expected_salt = client_creds.get_salt()
++
++ if any(etype in client_as_etypes and etype in initial_etypes
++ for etype in (kcrypto.Enctype.AES256,
++ kcrypto.Enctype.AES128,
++ kcrypto.Enctype.RC4)):
++ expected_error_mode = KDC_ERR_PREAUTH_REQUIRED
++ else:
++ expected_error_mode = KDC_ERR_ETYPE_NOSUPP
++
++ kdc_exchange_dict = self.as_exchange_dict(
++ expected_crealm=expected_crealm,
++ expected_cname=expected_cname,
++ expected_srealm=expected_srealm,
++ expected_sname=expected_sname,
++ generate_padata_fn=None,
++ check_error_fn=self.generic_check_kdc_error,
++ check_rep_fn=None,
++ expected_error_mode=expected_error_mode,
++ client_as_etypes=client_as_etypes,
++ expected_salt=expected_salt,
++ kdc_options=str(initial_kdc_options),
++ pac_request=pac)
++
++ self._generic_kdc_exchange(kdc_exchange_dict,
++ cname=cname,
++ realm=realm,
++ sname=sname,
++ etypes=initial_etypes)
++
++ def _test_as_req_no_preauth_with_args(self, etype_idx, pac):
++ name, etypes = self.etype_test_permutation_by_idx(etype_idx)
++ self._test_as_req_nopreauth(
++ pac=pac,
++ initial_etypes=etypes,
++ initial_kdc_options=krb5_asn1.KDCOptions('forwardable'))
++
++ def test_as_req_enc_timestamp(self):
++ client_creds = self.get_client_creds()
++ self._run_as_req_enc_timestamp(client_creds)
++
++ def test_as_req_enc_timestamp_mac(self):
++ client_creds = self.get_mach_creds()
++ self._run_as_req_enc_timestamp(client_creds)
++
++
+ if __name__ == "__main__":
+ global_asn1_print = False
+ global_hexdump = False
+diff --git python/samba/tests/krb5/salt_tests.py python/samba/tests/krb5/salt_tests.py
+index ecbf618e40e..db777f8b7bc 100755
+--- python/samba/tests/krb5/salt_tests.py
++++ python/samba/tests/krb5/salt_tests.py
+@@ -21,7 +21,7 @@ import os
+
+ import ldb
+
+-from samba.tests.krb5.as_req_tests import AsReqKerberosTests
++from samba.tests.krb5.as_req_tests import AsReqBaseTest
+ import samba.tests.krb5.kcrypto as kcrypto
+
+ sys.path.insert(0, "bin/python")
+@@ -31,7 +31,7 @@ global_asn1_print = False
+ global_hexdump = False
+
+
+-class SaltTests(AsReqKerberosTests):
++class SaltTests(AsReqBaseTest):
+
+ def setUp(self):
+ super().setUp()
+--
+2.25.1
+
+
+From 3d48ade670bb5b026d7bc0a26a4fa6775b21653b Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 16:02:00 +1300
+Subject: [PATCH 24/99] tests/krb5: Run test_rpc against member server
+
+We were instead always running against the DC.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 167bd2070483004cd0b9a96ffb40ea73c6ddf579)
+---
+ python/samba/tests/krb5/test_rpc.py | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git python/samba/tests/krb5/test_rpc.py python/samba/tests/krb5/test_rpc.py
+index 03c125f518a..2d483986e83 100755
+--- python/samba/tests/krb5/test_rpc.py
++++ python/samba/tests/krb5/test_rpc.py
+@@ -58,7 +58,7 @@ class RpcTests(KDCBaseTest):
+
+ samdb = self.get_samdb()
+
+- mach_name = samdb.host_dns_name()
++ mach_name = self.host
+ service = "cifs"
+
+ # Create the user account.
+@@ -67,7 +67,7 @@ class RpcTests(KDCBaseTest):
+ use_cache=False)
+ user_name = user_credentials.get_username()
+
+- mach_credentials = self.get_dc_creds()
++ mach_credentials = self.get_server_creds()
+
+ # Talk to the KDC to obtain the service ticket, which gets placed into
+ # the cache. The machine account name has to match the name in the
+@@ -114,8 +114,7 @@ class RpcTests(KDCBaseTest):
+ self.assertEqual(user_name, account_name.string)
+
+ def test_rpc_anonymous(self):
+- samdb = self.get_samdb()
+- mach_name = samdb.host_dns_name()
++ mach_name = self.host
+
+ anon_creds = credentials.Credentials()
+ anon_creds.set_anonymous()
+@@ -125,7 +124,7 @@ class RpcTests(KDCBaseTest):
+
+ (account_name, _) = conn.GetUserName(None, None, None)
+
+- self.assertEqual('ANONYMOUS LOGON', account_name.string)
++ self.assertEqual('ANONYMOUS LOGON', account_name.string.upper())
+
+
+ if __name__ == "__main__":
+--
+2.25.1
+
+
+From bf1aa0927895b1007ecea738681235b5be2e6208 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:37:35 +1300
+Subject: [PATCH 25/99] tests/krb5: Allow PasswordKey_create() to use s2kparams
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit a560c2e9ad8abb824d1805c86c656943745f81eb)
+---
+ python/samba/tests/krb5/raw_testcase.py | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git python/samba/tests/krb5/raw_testcase.py python/samba/tests/krb5/raw_testcase.py
+index 36a6134e6c9..da3f69c79c6 100644
+--- python/samba/tests/krb5/raw_testcase.py
++++ python/samba/tests/krb5/raw_testcase.py
+@@ -1167,10 +1167,11 @@ class RawKerberosTest(TestCaseInTempDir):
+ key = kcrypto.Key(etype, contents)
+ return RodcPacEncryptionKey(key, kvno)
+
+- def PasswordKey_create(self, etype=None, pwd=None, salt=None, kvno=None):
++ def PasswordKey_create(self, etype=None, pwd=None, salt=None, kvno=None,
++ params=None):
+ self.assertIsNotNone(pwd)
+ self.assertIsNotNone(salt)
+- key = kcrypto.string_to_key(etype, pwd, salt)
++ key = kcrypto.string_to_key(etype, pwd, salt, params=params)
+ return RodcPacEncryptionKey(key, kvno)
+
+ def PasswordKey_from_etype_info2(self, creds, etype_info2, kvno=None):
+@@ -1182,9 +1183,11 @@ class RawKerberosTest(TestCaseInTempDir):
+ nthash = creds.get_nt_hash()
+ return self.SessionKey_create(etype=e, contents=nthash, kvno=kvno)
+
++ params = etype_info2.get('s2kparams')
++
+ password = creds.get_password()
+ return self.PasswordKey_create(
+- etype=e, pwd=password, salt=salt, kvno=kvno)
++ etype=e, pwd=password, salt=salt, kvno=kvno, params=params)
+
+ def TicketDecryptionKey_from_creds(self, creds, etype=None):
+
+--
+2.25.1
+
+
+From 651db77b1c19c036cf229c44b764b0155e1dc399 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:40:35 +1300
+Subject: [PATCH 26/99] tests/krb5: Split out methods to create renewable or
+ invalid tickets
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit e930274aa43810d6485c3c8a7c82958ecb409630)
+---
+ python/samba/tests/krb5/kdc_tgs_tests.py | 68 +++++++++++++-----------
+ 1 file changed, 36 insertions(+), 32 deletions(-)
+
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index abac5a47a56..0578969ba69 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -1786,6 +1786,40 @@ class KdcTgsTests(KDCBaseTest):
+
+ self._run_tgs(tgt, expected_error=KDC_ERR_C_PRINCIPAL_UNKNOWN)
+
++ def _modify_renewable(self, enc_part):
++ # Set the renewable flag.
++ renewable_flag = krb5_asn1.TicketFlags('renewable')
++ pos = len(tuple(renewable_flag)) - 1
++
++ flags = enc_part['flags']
++ self.assertLessEqual(pos, len(flags))
++
++ new_flags = flags[:pos] + '1' + flags[pos + 1:]
++ enc_part['flags'] = new_flags
++
++ # Set the renew-till time to be in the future.
++ renew_till = self.get_KerberosTime(offset=100 * 60 * 60)
++ enc_part['renew-till'] = renew_till
++
++ return enc_part
++
++ def _modify_invalid(self, enc_part):
++ # Set the invalid flag.
++ invalid_flag = krb5_asn1.TicketFlags('invalid')
++ pos = len(tuple(invalid_flag)) - 1
++
++ flags = enc_part['flags']
++ self.assertLessEqual(pos, len(flags))
++
++ new_flags = flags[:pos] + '1' + flags[pos + 1:]
++ enc_part['flags'] = new_flags
++
++ # Set the ticket start time to be in the past.
++ past_time = self.get_KerberosTime(offset=-100 * 60 * 60)
++ enc_part['starttime'] = past_time
++
++ return enc_part
++
+ def _get_tgt(self,
+ client_creds,
+ renewable=False,
+@@ -1880,39 +1914,9 @@ class KdcTgsTests(KDCBaseTest):
+ }
+
+ if renewable:
+- def flags_modify_fn(enc_part):
+- # Set the renewable flag.
+- renewable_flag = krb5_asn1.TicketFlags('renewable')
+- pos = len(tuple(renewable_flag)) - 1
+-
+- flags = enc_part['flags']
+- self.assertLessEqual(pos, len(flags))
+-
+- new_flags = flags[:pos] + '1' + flags[pos + 1:]
+- enc_part['flags'] = new_flags
+-
+- # Set the renew-till time to be in the future.
+- renew_till = self.get_KerberosTime(offset=100 * 60 * 60)
+- enc_part['renew-till'] = renew_till
+-
+- return enc_part
++ flags_modify_fn = self._modify_renewable
+ elif invalid:
+- def flags_modify_fn(enc_part):
+- # Set the invalid flag.
+- invalid_flag = krb5_asn1.TicketFlags('invalid')
+- pos = len(tuple(invalid_flag)) - 1
+-
+- flags = enc_part['flags']
+- self.assertLessEqual(pos, len(flags))
+-
+- new_flags = flags[:pos] + '1' + flags[pos + 1:]
+- enc_part['flags'] = new_flags
+-
+- # Set the ticket start time to be in the past.
+- past_time = self.get_KerberosTime(offset=-100 * 60 * 60)
+- enc_part['starttime'] = past_time
+-
+- return enc_part
++ flags_modify_fn = self._modify_invalid
+ else:
+ flags_modify_fn = None
+
+--
+2.25.1
+
+
+From 1e9ad4246ce7fe7a212da4357e6e11c5ac22a8b2 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:52:31 +1300
+Subject: [PATCH 27/99] tests/krb5: Adjust error codes to better match Windows
+ with PacRequestorEnforcement=2
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit d95705172bcf6fe24817800a4c0009e9cc8be595)
+
+[jsutton@samba.org Fixed MIT knownfail conflict]
+---
+ python/samba/tests/krb5/alias_tests.py | 7 +-
+ python/samba/tests/krb5/kdc_tgs_tests.py | 130 ++++++++----------
+ .../ms_kile_client_principal_lookup_tests.py | 39 ++----
+ python/samba/tests/krb5/s4u_tests.py | 57 ++++----
+ python/samba/tests/krb5/test_rpc.py | 8 +-
+ selftest/knownfail_heimdal_kdc | 64 +++++++++
+ selftest/knownfail_mit_kdc | 9 ++
+ 7 files changed, 181 insertions(+), 133 deletions(-)
+
+diff --git python/samba/tests/krb5/alias_tests.py python/samba/tests/krb5/alias_tests.py
+index 60213845a44..1f63775c189 100755
+--- python/samba/tests/krb5/alias_tests.py
++++ python/samba/tests/krb5/alias_tests.py
+@@ -28,7 +28,7 @@ from samba.tests.krb5.kdc_base_test import KDCBaseTest
+ from samba.tests.krb5.rfc4120_constants import (
+ AES256_CTS_HMAC_SHA1_96,
+ ARCFOUR_HMAC_MD5,
+- KDC_ERR_CLIENT_NAME_MISMATCH,
++ KDC_ERR_TGT_REVOKED,
+ NT_PRINCIPAL,
+ )
+
+@@ -168,7 +168,7 @@ class AliasTests(KDCBaseTest):
+ ctype=None)
+ return [padata], req_body
+
+- expected_error_mode = KDC_ERR_CLIENT_NAME_MISMATCH
++ expected_error_mode = KDC_ERR_TGT_REVOKED
+
+ # Make a request using S4U2Self. The request should fail.
+ kdc_exchange_dict = self.tgs_exchange_dict(
+@@ -184,7 +184,8 @@ class AliasTests(KDCBaseTest):
+ tgt=tgt,
+ authenticator_subkey=authenticator_subkey,
+ kdc_options='0',
+- expect_pac=True)
++ expect_pac=True,
++ expect_edata=False)
+
+ rep = self._generic_kdc_exchange(kdc_exchange_dict,
+ cname=None,
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index 0578969ba69..7ea15f0fbab 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -23,7 +23,7 @@ import os
+ import ldb
+
+
+-from samba import dsdb, ntstatus
++from samba import dsdb
+
+ from samba.dcerpc import krb5pac, security
+
+@@ -38,8 +38,6 @@ from samba.tests.krb5.rfc4120_constants import (
+ KRB_ERROR,
+ KRB_TGS_REP,
+ KDC_ERR_BADMATCH,
+- KDC_ERR_BADOPTION,
+- KDC_ERR_CLIENT_NAME_MISMATCH,
+ KDC_ERR_GENERIC,
+ KDC_ERR_MODIFIED,
+ KDC_ERR_POLICY,
+@@ -262,7 +260,7 @@ class KdcTgsTests(KDCBaseTest):
+ authenticator_subkey = self.RandomKey(kcrypto.Enctype.AES256)
+
+ if expect_error:
+- expected_error_mode = KDC_ERR_BADOPTION
++ expected_error_mode = KDC_ERR_TGT_REVOKED
+ check_error_fn = self.generic_check_kdc_error
+ check_rep_fn = None
+ else:
+@@ -288,7 +286,8 @@ class KdcTgsTests(KDCBaseTest):
+ authenticator_subkey=authenticator_subkey,
+ kdc_options=kdc_options,
+ pac_request=pac_request,
+- expect_pac=expect_pac)
++ expect_pac=expect_pac,
++ expect_edata=False)
+
+ rep = self._generic_kdc_exchange(kdc_exchange_dict,
+ cname=cname,
+@@ -516,8 +515,7 @@ class KdcTgsTests(KDCBaseTest):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, remove_requester_sid=True)
+
+- self._run_tgs(tgt, expected_error=0, expect_pac=True,
+- expect_requester_sid=False) # Note: not expected
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_tgs_req_no_pac_attrs(self):
+ creds = self._get_creds()
+@@ -531,11 +529,7 @@ class KdcTgsTests(KDCBaseTest):
+ revealed_to_rodc=True)
+ tgt = self._get_tgt(creds, from_rodc=True, remove_requester_sid=True)
+
+- samdb = self.get_samdb()
+- sid = self.get_objectSid(samdb, creds.get_dn())
+-
+- self._run_tgs(tgt, expected_error=0, expect_pac=True,
+- expect_requester_sid=True, expected_sid=sid)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_tgs_req_from_rodc_no_pac_attrs(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -548,101 +542,99 @@ class KdcTgsTests(KDCBaseTest):
+ def test_tgs_no_pac(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, remove_pac=True)
+- self._run_tgs(tgt, expected_error=KDC_ERR_BADOPTION)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_renew_no_pac(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, renewable=True, remove_pac=True)
+- self._renew_tgt(tgt, expected_error=KDC_ERR_BADOPTION)
++ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_validate_no_pac(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, invalid=True, remove_pac=True)
+- self._validate_tgt(tgt, expected_error=KDC_ERR_BADOPTION)
++ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_s4u2self_no_pac(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, remove_pac=True)
+ self._s4u2self(tgt, creds,
+- expected_error=(KDC_ERR_GENERIC, KDC_ERR_BADOPTION),
+- expected_status=ntstatus.NT_STATUS_INVALID_PARAMETER,
+- expect_edata=True)
++ expected_error=KDC_ERR_TGT_REVOKED,
++ expect_edata=False)
+
+ def test_user2user_no_pac(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, remove_pac=True)
+- self._user2user(tgt, creds, expected_error=KDC_ERR_BADOPTION)
++ self._user2user(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED)
+
+ # Test making a request with authdata and without a PAC.
+ def test_tgs_authdata_no_pac(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, remove_pac=True, allow_empty_authdata=True)
+- self._run_tgs(tgt, expected_error=KDC_ERR_BADOPTION)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_renew_authdata_no_pac(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, renewable=True, remove_pac=True,
+ allow_empty_authdata=True)
+- self._renew_tgt(tgt, expected_error=KDC_ERR_BADOPTION)
++ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_validate_authdata_no_pac(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, invalid=True, remove_pac=True,
+ allow_empty_authdata=True)
+- self._validate_tgt(tgt, expected_error=KDC_ERR_BADOPTION)
++ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_s4u2self_authdata_no_pac(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, remove_pac=True, allow_empty_authdata=True)
+ self._s4u2self(tgt, creds,
+- expected_error=(KDC_ERR_GENERIC, KDC_ERR_BADOPTION),
+- expected_status=ntstatus.NT_STATUS_INVALID_PARAMETER,
+- expect_edata=True)
++ expected_error=KDC_ERR_TGT_REVOKED,
++ expect_edata=False)
+
+ def test_user2user_authdata_no_pac(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, remove_pac=True, allow_empty_authdata=True)
+- self._user2user(tgt, creds, expected_error=KDC_ERR_BADOPTION)
++ self._user2user(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED)
+
+ # Test changing the SID in the PAC to that of another account.
+ def test_tgs_sid_mismatch_existing(self):
+ creds = self._get_creds()
+ existing_rid = self._get_existing_rid()
+ tgt = self._get_tgt(creds, new_rid=existing_rid)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_renew_sid_mismatch_existing(self):
+ creds = self._get_creds()
+ existing_rid = self._get_existing_rid()
+ tgt = self._get_tgt(creds, renewable=True, new_rid=existing_rid)
+- self._renew_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_validate_sid_mismatch_existing(self):
+ creds = self._get_creds()
+ existing_rid = self._get_existing_rid()
+ tgt = self._get_tgt(creds, invalid=True, new_rid=existing_rid)
+- self._validate_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_s4u2self_sid_mismatch_existing(self):
+ creds = self._get_creds()
+ existing_rid = self._get_existing_rid()
+ tgt = self._get_tgt(creds, new_rid=existing_rid)
+ self._s4u2self(tgt, creds,
+- expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_user2user_sid_mismatch_existing(self):
+ creds = self._get_creds()
+ existing_rid = self._get_existing_rid()
+ tgt = self._get_tgt(creds, new_rid=existing_rid)
+ self._user2user(tgt, creds,
+- expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_requester_sid_mismatch_existing(self):
+ creds = self._get_creds()
+ existing_rid = self._get_existing_rid()
+ tgt = self._get_tgt(creds, new_rid=existing_rid,
+ can_modify_logon_info=False)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_logon_info_sid_mismatch_existing(self):
+ creds = self._get_creds()
+@@ -656,49 +648,49 @@ class KdcTgsTests(KDCBaseTest):
+ existing_rid = self._get_existing_rid()
+ tgt = self._get_tgt(creds, new_rid=existing_rid,
+ remove_requester_sid=True)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ # Test changing the SID in the PAC to a non-existent one.
+ def test_tgs_sid_mismatch_nonexisting(self):
+ creds = self._get_creds()
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, new_rid=nonexistent_rid)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_renew_sid_mismatch_nonexisting(self):
+ creds = self._get_creds()
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, renewable=True,
+ new_rid=nonexistent_rid)
+- self._renew_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_validate_sid_mismatch_nonexisting(self):
+ creds = self._get_creds()
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, invalid=True,
+ new_rid=nonexistent_rid)
+- self._validate_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_s4u2self_sid_mismatch_nonexisting(self):
+ creds = self._get_creds()
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, new_rid=nonexistent_rid)
+ self._s4u2self(tgt, creds,
+- expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_user2user_sid_mismatch_nonexisting(self):
+ creds = self._get_creds()
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, new_rid=nonexistent_rid)
+ self._user2user(tgt, creds,
+- expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_requester_sid_mismatch_nonexisting(self):
+ creds = self._get_creds()
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, new_rid=nonexistent_rid,
+ can_modify_logon_info=False)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_logon_info_sid_mismatch_nonexisting(self):
+ creds = self._get_creds()
+@@ -712,7 +704,7 @@ class KdcTgsTests(KDCBaseTest):
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, new_rid=nonexistent_rid,
+ remove_requester_sid=True)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ # Test with an RODC-issued ticket where the client is revealed to the RODC.
+ def test_tgs_rodc_revealed(self):
+@@ -753,7 +745,7 @@ class KdcTgsTests(KDCBaseTest):
+ existing_rid = self._get_existing_rid(replication_allowed=True,
+ revealed_to_rodc=True)
+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_renew_rodc_sid_mismatch_existing(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -762,7 +754,7 @@ class KdcTgsTests(KDCBaseTest):
+ revealed_to_rodc=True)
+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True,
+ new_rid=existing_rid)
+- self._renew_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_validate_rodc_sid_mismatch_existing(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -771,7 +763,7 @@ class KdcTgsTests(KDCBaseTest):
+ revealed_to_rodc=True)
+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True,
+ new_rid=existing_rid)
+- self._validate_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_s4u2self_rodc_sid_mismatch_existing(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -779,7 +771,7 @@ class KdcTgsTests(KDCBaseTest):
+ existing_rid = self._get_existing_rid(replication_allowed=True,
+ revealed_to_rodc=True)
+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid)
+- self._s4u2self(tgt, creds, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._s4u2self(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_user2user_rodc_sid_mismatch_existing(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -788,7 +780,7 @@ class KdcTgsTests(KDCBaseTest):
+ revealed_to_rodc=True)
+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid)
+ self._user2user(tgt, creds,
+- expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_tgs_rodc_requester_sid_mismatch_existing(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -797,7 +789,7 @@ class KdcTgsTests(KDCBaseTest):
+ revealed_to_rodc=True)
+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid,
+ can_modify_logon_info=False)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_tgs_rodc_logon_info_sid_mismatch_existing(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -815,7 +807,7 @@ class KdcTgsTests(KDCBaseTest):
+ revealed_to_rodc=True)
+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid,
+ remove_requester_sid=True)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ # Test with an RODC-issued ticket where the SID in the PAC is changed to a
+ # non-existent one.
+@@ -824,7 +816,7 @@ class KdcTgsTests(KDCBaseTest):
+ revealed_to_rodc=True)
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_renew_rodc_sid_mismatch_nonexisting(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -832,7 +824,7 @@ class KdcTgsTests(KDCBaseTest):
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True,
+ new_rid=nonexistent_rid)
+- self._renew_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_validate_rodc_sid_mismatch_nonexisting(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -840,14 +832,14 @@ class KdcTgsTests(KDCBaseTest):
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True,
+ new_rid=nonexistent_rid)
+- self._validate_tgt(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_s4u2self_rodc_sid_mismatch_nonexisting(self):
+ creds = self._get_creds(replication_allowed=True,
+ revealed_to_rodc=True)
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid)
+- self._s4u2self(tgt, creds, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._s4u2self(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_user2user_rodc_sid_mismatch_nonexisting(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -855,7 +847,7 @@ class KdcTgsTests(KDCBaseTest):
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid)
+ self._user2user(tgt, creds,
+- expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_tgs_rodc_requester_sid_mismatch_nonexisting(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -863,7 +855,7 @@ class KdcTgsTests(KDCBaseTest):
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid,
+ can_modify_logon_info=False)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_tgs_rodc_logon_info_sid_mismatch_nonexisting(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -879,7 +871,7 @@ class KdcTgsTests(KDCBaseTest):
+ nonexistent_rid = self._get_non_existent_rid()
+ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid,
+ remove_requester_sid=True)
+- self._run_tgs(tgt, expected_error=KDC_ERR_CLIENT_NAME_MISMATCH)
++ self._run_tgs(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ # Test with an RODC-issued ticket where the client is not revealed to the
+ # RODC.
+@@ -1111,8 +1103,7 @@ class KdcTgsTests(KDCBaseTest):
+ names=[user_name])
+
+ self._user2user(tgt, creds, sname=sname,
+- expected_error=(KDC_ERR_BADMATCH,
+- KDC_ERR_BADOPTION))
++ expected_error=KDC_ERR_BADMATCH)
+
+ def test_user2user_other_sname(self):
+ other_name = self.get_new_username()
+@@ -1134,8 +1125,7 @@ class KdcTgsTests(KDCBaseTest):
+ sname = self.get_krbtgt_sname()
+
+ self._user2user(tgt, creds, sname=sname,
+- expected_error=(KDC_ERR_BADMATCH,
+- KDC_ERR_BADOPTION))
++ expected_error=KDC_ERR_BADMATCH)
+
+ def test_user2user_wrong_srealm(self):
+ creds = self._get_creds()
+@@ -1206,7 +1196,9 @@ class KdcTgsTests(KDCBaseTest):
+
+ tgt = self._modify_tgt(tgt, cname=cname)
+
+- self._user2user(tgt, creds, expected_error=KDC_ERR_C_PRINCIPAL_UNKNOWN)
++ self._user2user(tgt, creds,
++ expected_error=(KDC_ERR_TGT_REVOKED,
++ KDC_ERR_C_PRINCIPAL_UNKNOWN))
+
+ def test_user2user_non_existent_sname(self):
+ creds = self._get_creds()
+@@ -1522,8 +1514,7 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._modify_tgt(tgt, renewable=True,
+ remove_requester_sid=True)
+
+- self._renew_tgt(tgt, expected_error=0, expect_pac=True,
+- expect_requester_sid=False) # Note: not expected
++ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_tgs_requester_sid_missing_rodc_renew(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -1539,9 +1530,7 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._modify_tgt(tgt, from_rodc=True, renewable=True,
+ remove_requester_sid=True)
+
+- self._renew_tgt(tgt, expected_error=0, expect_pac=True,
+- expected_sid=sid,
+- expect_requester_sid=True)
++ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
+ def test_tgs_pac_request_none(self):
+ creds = self._get_creds()
+@@ -1655,10 +1644,10 @@ class KdcTgsTests(KDCBaseTest):
+ creds = self._get_creds()
+ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None)
+
+- ticket = self._s4u2self(tgt, creds, expected_error=0, expect_pac=False)
++ ticket = self._s4u2self(tgt, creds, expected_error=0, expect_pac=True)
+
+- pac = self.get_ticket_pac(ticket, expect_pac=False)
+- self.assertIsNone(pac)
++ pac = self.get_ticket_pac(ticket)
++ self.assertIsNotNone(pac)
+
+ def test_s4u2self_pac_request_true(self):
+ creds = self._get_creds()
+@@ -1753,10 +1742,10 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None)
+ tgt = self._modify_tgt(tgt, from_rodc=True)
+
+- ticket = self._run_tgs(tgt, expected_error=0, expect_pac=False)
++ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
+
+ pac = self.get_ticket_pac(ticket, expect_pac=False)
+- self.assertIsNone(pac)
++ self.assertIsNotNone(pac)
+
+ def test_tgs_rodc_pac_request_true(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -1784,7 +1773,8 @@ class KdcTgsTests(KDCBaseTest):
+ 'sAMAccountName')
+ samdb.modify(msg)
+
+- self._run_tgs(tgt, expected_error=KDC_ERR_C_PRINCIPAL_UNKNOWN)
++ self._run_tgs(tgt, expected_error=(KDC_ERR_TGT_REVOKED,
++ KDC_ERR_C_PRINCIPAL_UNKNOWN))
+
+ def _modify_renewable(self, enc_part):
+ # Set the renewable flag.
+diff --git python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py
+index 0aa3309b814..e6b90d3e16a 100755
+--- python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py
++++ python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py
+@@ -32,6 +32,7 @@ from samba.tests.krb5.rfc4120_constants import (
+ NT_PRINCIPAL,
+ NT_SRV_INST,
+ KDC_ERR_C_PRINCIPAL_UNKNOWN,
++ KDC_ERR_TGT_REVOKED,
+ )
+
+ global_asn1_print = False
+@@ -322,21 +323,10 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest):
+
+ (rep, enc_part) = self.tgs_req(
+ cname, sname, uc.get_realm(), ticket, key, etype,
+- service_creds=mc, expect_pac=False)
+- self.check_tgs_reply(rep)
+-
+- # Check the contents of the service ticket
+- ticket = rep['ticket']
+- enc_part = self.decode_service_ticket(mc, ticket)
+- #
+- # We get an empty authorization-data element in the ticket.
+- # i.e. no PAC
+- self.assertEqual([], enc_part['authorization-data'])
+- # check the crealm and cname
+- cname = enc_part['cname']
+- self.assertEqual(NT_PRINCIPAL, cname['name-type'])
+- self.assertEqual(alt_name.encode('UTF8'), cname['name-string'][0])
+- self.assertEqual(realm.upper().encode('UTF8'), enc_part['crealm'])
++ service_creds=mc, expect_pac=False,
++ expect_edata=False,
++ expected_error_mode=KDC_ERR_TGT_REVOKED)
++ self.check_error_rep(rep, KDC_ERR_TGT_REVOKED)
+
+ def test_nt_principal_step_4_b(self):
+ ''' Step 4, pre-authentication
+@@ -703,21 +693,10 @@ class MS_Kile_Client_Principal_Lookup_Tests(KDCBaseTest):
+
+ (rep, enc_part) = self.tgs_req(
+ cname, sname, uc.get_realm(), ticket, key, etype,
+- service_creds=mc, expect_pac=False)
+- self.check_tgs_reply(rep)
+-
+- # Check the contents of the service ticket
+- ticket = rep['ticket']
+- enc_part = self.decode_service_ticket(mc, ticket)
+- #
+- # We get an empty authorization-data element in the ticket.
+- # i.e. no PAC
+- self.assertEqual([], enc_part['authorization-data'])
+- # check the crealm and cname
+- cname = enc_part['cname']
+- self.assertEqual(NT_ENTERPRISE_PRINCIPAL, cname['name-type'])
+- self.assertEqual(ename.encode('UTF8'), cname['name-string'][0])
+- self.assertEqual(realm.upper().encode('UTF8'), enc_part['crealm'])
++ service_creds=mc, expect_pac=False,
++ expect_edata=False,
++ expected_error_mode=KDC_ERR_TGT_REVOKED)
++ self.check_error_rep(rep, KDC_ERR_TGT_REVOKED)
+
+ def test_nt_enterprise_principal_step_6_b(self):
+ ''' Step 4, pre-authentication
+diff --git python/samba/tests/krb5/s4u_tests.py python/samba/tests/krb5/s4u_tests.py
+index a80a7b3427e..5f37525f393 100755
+--- python/samba/tests/krb5/s4u_tests.py
++++ python/samba/tests/krb5/s4u_tests.py
+@@ -42,6 +42,7 @@ from samba.tests.krb5.rfc4120_constants import (
+ KDC_ERR_INAPP_CKSUM,
+ KDC_ERR_MODIFIED,
+ KDC_ERR_SUMTYPE_NOSUPP,
++ KDC_ERR_TGT_REVOKED,
+ KU_PA_ENC_TIMESTAMP,
+ KU_AS_REP_ENC_PART,
+ KU_TGS_REP_ENC_PART_SUB_KEY,
+@@ -278,6 +279,8 @@ class S4UKerberosTests(KDCBaseTest):
+ etypes = kdc_dict.pop('etypes', (AES256_CTS_HMAC_SHA1_96,
+ ARCFOUR_HMAC_MD5))
+
++ expect_edata = kdc_dict.pop('expect_edata', None)
++
+ def generate_s4u2self_padata(_kdc_exchange_dict,
+ _callback_dict,
+ req_body):
+@@ -309,7 +312,8 @@ class S4UKerberosTests(KDCBaseTest):
+ tgt=service_tgt,
+ authenticator_subkey=authenticator_subkey,
+ kdc_options=str(kdc_options),
+- expect_claims=False)
++ expect_claims=False,
++ expect_edata=expect_edata)
+
+ self._generic_kdc_exchange(kdc_exchange_dict,
+ cname=None,
+@@ -343,15 +347,14 @@ class S4UKerberosTests(KDCBaseTest):
+
+ self._run_s4u2self_test(
+ {
+- 'expected_error_mode': (KDC_ERR_GENERIC,
+- KDC_ERR_BADOPTION),
+- 'expected_status': ntstatus.NT_STATUS_INVALID_PARAMETER,
++ 'expected_error_mode': KDC_ERR_TGT_REVOKED,
+ 'client_opts': {
+ 'not_delegated': False
+ },
+ 'kdc_options': 'forwardable',
+ 'modify_service_tgt_fn': forwardable_no_pac,
+- 'expected_flags': 'forwardable'
++ 'expected_flags': 'forwardable',
++ 'expect_edata': False
+ })
+
+ # Test performing an S4U2Self operation without requesting a forwardable
+@@ -674,8 +677,8 @@ class S4UKerberosTests(KDCBaseTest):
+ # contain a PAC.
+ self._run_delegation_test(
+ {
+- 'expected_error_mode': (KDC_ERR_BADOPTION,
+- KDC_ERR_MODIFIED),
++ 'expected_error_mode': (KDC_ERR_MODIFIED,
++ KDC_ERR_TGT_REVOKED),
+ 'allow_delegation': True,
+ 'modify_client_tkt_fn': self.remove_ticket_pac,
+ 'expect_edata': False
+@@ -686,9 +689,10 @@ class S4UKerberosTests(KDCBaseTest):
+ # PAC.
+ self._run_delegation_test(
+ {
+- 'expected_error_mode': 0,
++ 'expected_error_mode': KDC_ERR_TGT_REVOKED,
+ 'allow_delegation': True,
+- 'modify_service_tgt_fn': self.remove_ticket_pac
++ 'modify_service_tgt_fn': self.remove_ticket_pac,
++ 'expect_edata': False
+ })
+
+ def test_constrained_delegation_no_client_pac_no_auth_data_required(self):
+@@ -696,8 +700,8 @@ class S4UKerberosTests(KDCBaseTest):
+ # contain a PAC.
+ self._run_delegation_test(
+ {
+- 'expected_error_mode': (KDC_ERR_BADOPTION,
+- KDC_ERR_MODIFIED),
++ 'expected_error_mode': (KDC_ERR_MODIFIED,
++ KDC_ERR_BADOPTION),
+ 'allow_delegation': True,
+ 'modify_client_tkt_fn': self.remove_ticket_pac,
+ 'expect_edata': False,
+@@ -711,13 +715,14 @@ class S4UKerberosTests(KDCBaseTest):
+ # PAC.
+ self._run_delegation_test(
+ {
+- 'expected_error_mode': (KDC_ERR_BADOPTION,
+- KDC_ERR_MODIFIED),
++ 'expected_error_mode': KDC_ERR_TGT_REVOKED,
+ 'allow_delegation': True,
+ 'modify_service_tgt_fn': self.remove_ticket_pac,
+ 'service2_opts': {
+ 'no_auth_data_required': True
+- }
++ },
++ 'expect_pac': False,
++ 'expect_edata': False
+ })
+
+ def test_constrained_delegation_non_forwardable(self):
+@@ -812,12 +817,11 @@ class S4UKerberosTests(KDCBaseTest):
+ # PAC.
+ self._run_delegation_test(
+ {
+- 'expected_error_mode': KDC_ERR_BADOPTION,
+- 'expected_status':
+- ntstatus.NT_STATUS_NOT_FOUND,
++ 'expected_error_mode': KDC_ERR_TGT_REVOKED,
+ 'allow_rbcd': True,
+ 'pac_options': '0001', # supports RBCD
+- 'modify_service_tgt_fn': self.remove_ticket_pac
++ 'modify_service_tgt_fn': self.remove_ticket_pac,
++ 'expect_edata': False
+ })
+
+ def test_rbcd_no_client_pac_no_auth_data_required_a(self):
+@@ -858,15 +862,14 @@ class S4UKerberosTests(KDCBaseTest):
+ # PAC.
+ self._run_delegation_test(
+ {
+- 'expected_error_mode': KDC_ERR_BADOPTION,
+- 'expected_status':
+- ntstatus.NT_STATUS_NOT_FOUND,
++ 'expected_error_mode': KDC_ERR_TGT_REVOKED,
+ 'allow_rbcd': True,
+ 'pac_options': '0001', # supports RBCD
+ 'modify_service_tgt_fn': self.remove_ticket_pac,
+ 'service2_opts': {
+ 'no_auth_data_required': True
+- }
++ },
++ 'expect_edata': False
+ })
+
+ def test_rbcd_non_forwardable(self):
+@@ -941,8 +944,8 @@ class S4UKerberosTests(KDCBaseTest):
+ for checksum in self.pac_checksum_types:
+ with self.subTest(checksum=checksum):
+ if checksum == krb5pac.PAC_TYPE_TICKET_CHECKSUM:
+- expected_error_mode = (KDC_ERR_BADOPTION,
+- KDC_ERR_MODIFIED)
++ expected_error_mode = (KDC_ERR_MODIFIED,
++ KDC_ERR_BADOPTION)
+ else:
+ expected_error_mode = KDC_ERR_GENERIC
+
+@@ -1061,8 +1064,7 @@ class S4UKerberosTests(KDCBaseTest):
+ for checksum in self.pac_checksum_types:
+ with self.subTest(checksum=checksum):
+ if checksum == krb5pac.PAC_TYPE_SRV_CHECKSUM:
+- expected_error_mode = (KDC_ERR_MODIFIED,
+- KDC_ERR_BAD_INTEGRITY)
++ expected_error_mode = KDC_ERR_MODIFIED
+ expected_status = ntstatus.NT_STATUS_WRONG_PASSWORD
+ else:
+ expected_error_mode = 0
+@@ -1162,8 +1164,7 @@ class S4UKerberosTests(KDCBaseTest):
+ with self.subTest(checksum=checksum, ctype=ctype):
+ if checksum == krb5pac.PAC_TYPE_SRV_CHECKSUM:
+ if ctype == Cksumtype.SHA1:
+- expected_error_mode = (KDC_ERR_SUMTYPE_NOSUPP,
+- KDC_ERR_BAD_INTEGRITY)
++ expected_error_mode = KDC_ERR_SUMTYPE_NOSUPP
+ expected_status = ntstatus.NT_STATUS_LOGON_FAILURE
+ else:
+ expected_error_mode = KDC_ERR_GENERIC
+diff --git python/samba/tests/krb5/test_rpc.py python/samba/tests/krb5/test_rpc.py
+index 2d483986e83..5a3c7339cea 100755
+--- python/samba/tests/krb5/test_rpc.py
++++ python/samba/tests/krb5/test_rpc.py
+@@ -24,7 +24,10 @@ import ldb
+
+ from samba import NTSTATUSError, credentials
+ from samba.dcerpc import lsa
+-from samba.ntstatus import NT_STATUS_NO_IMPERSONATION_TOKEN
++from samba.ntstatus import (
++ NT_STATUS_ACCESS_DENIED,
++ NT_STATUS_NO_IMPERSONATION_TOKEN
++)
+
+ from samba.tests.krb5.kdc_base_test import KDCBaseTest
+
+@@ -103,7 +106,8 @@ class RpcTests(KDCBaseTest):
+ self.fail()
+
+ enum, _ = e.args
+- self.assertEqual(NT_STATUS_NO_IMPERSONATION_TOKEN, enum)
++ self.assertIn(enum, {NT_STATUS_ACCESS_DENIED,
++ NT_STATUS_NO_IMPERSONATION_TOKEN})
+ return
+
+ (account_name, _) = conn.GetUserName(None, None, None)
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 7eba899966e..1b7e159c381 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -233,16 +233,21 @@
+ # S4U tests
+ #
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_rbcd_old_checksum
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_client_pac(?!_no_auth_data_required)
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_service_pac\(.*\)$
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_service_pac_no_auth_data_required
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_existing_delegation_info
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_missing_client_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_a
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_b
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_service_pac
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_service_pac_no_auth_data_required
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_unkeyed_client_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_unkeyed_service_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_client_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_service_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_no_pac
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed
+ #
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_auth_data_required
+@@ -259,3 +264,62 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_not_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_not_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_not_revealed
++#
++# Alias tests
++#
++^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_delete
++^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_rename
++^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_delete
++^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_rename
++#
++# KDC TGS tests
++#
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_client_no_auth_data_required
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_service_no_auth_data_required
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_authdata_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_authdata_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_false
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_authdata_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_no_requester_sid
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_renew
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_false
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname_krbtgt
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_authdata_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_nonexisting
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 8cd36fe2d96..cc12499bb50 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -390,6 +390,8 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ #
+ # KDC TGT tests
+ #
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_allowed_denied
+@@ -401,6 +403,8 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_req
+@@ -418,6 +422,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rename
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_allowed_denied
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_denied
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_no_krbtgt_link
+@@ -427,6 +432,8 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_sname
+@@ -462,6 +469,8 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_nonexisting
+ #
+ # PAC attributes tests
+ #
+--
+2.25.1
+
+
+From ea82822a5c451df50feed15c5da3501df2b5c106 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 12:04:36 +1300
+Subject: [PATCH 28/99] tests/krb5: Remove unnecessary expect_pac arguments
+
+The value of expect_pac is not considered if we are expecting an error.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 28d501875a98fa2817262eb8ec68bf91528428c2)
+---
+ python/samba/tests/krb5/kdc_tgs_tests.py | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index 7ea15f0fbab..6160ef649e8 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -412,7 +412,7 @@ class KdcTgsTests(KDCBaseTest):
+ self.assertIsNone(pac)
+
+ self._make_tgs_request(client_creds, service_creds, tgt,
+- expect_pac=False, expect_error=True)
++ expect_error=True)
+
+ def test_remove_pac_client_no_auth_data_required(self):
+ client_creds = self.get_cached_creds(
+@@ -427,7 +427,7 @@ class KdcTgsTests(KDCBaseTest):
+ self.assertIsNone(pac)
+
+ self._make_tgs_request(client_creds, service_creds, tgt,
+- expect_pac=False, expect_error=True)
++ expect_error=True)
+
+ def test_remove_pac(self):
+ client_creds = self.get_client_creds()
+@@ -440,7 +440,7 @@ class KdcTgsTests(KDCBaseTest):
+ self.assertIsNone(pac)
+
+ self._make_tgs_request(client_creds, service_creds, tgt,
+- expect_pac=False, expect_error=True)
++ expect_error=True)
+
+ def test_upn_dns_info_ex_user(self):
+ client_creds = self.get_client_creds()
+--
+2.25.1
+
+
+From eb0ed5f4f6d725c49fda97bc8f7aae89f90bd913 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 30 Nov 2021 09:26:40 +1300
+Subject: [PATCH 29/99] tests/krb5: Add tests for invalid TGTs
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 7574ba9f580fca552b80532a49d00e657fbdf4fd)
+
+[jsutton@samba.org Removed some MIT knownfail changes]
+---
+ python/samba/tests/krb5/kdc_tgs_tests.py | 16 ++++++++++++++++
+ python/samba/tests/krb5/rfc4120_constants.py | 1 +
+ selftest/knownfail_mit_kdc | 1 +
+ 3 files changed, 18 insertions(+)
+
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index 6160ef649e8..f5f091610ac 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -44,6 +44,7 @@ from samba.tests.krb5.rfc4120_constants import (
+ KDC_ERR_C_PRINCIPAL_UNKNOWN,
+ KDC_ERR_S_PRINCIPAL_UNKNOWN,
+ KDC_ERR_TGT_REVOKED,
++ KRB_ERR_TKT_NYV,
+ KDC_ERR_WRONG_REALM,
+ NT_PRINCIPAL,
+ NT_SRV_INST,
+@@ -511,6 +512,21 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._get_tgt(creds)
+ self._user2user(tgt, creds, expected_error=0)
+
++ def test_tgs_req_invalid(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds, invalid=True)
++ self._run_tgs(tgt, expected_error=KRB_ERR_TKT_NYV)
++
++ def test_s4u2self_req_invalid(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds, invalid=True)
++ self._s4u2self(tgt, creds, expected_error=KRB_ERR_TKT_NYV)
++
++ def test_user2user_req_invalid(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds, invalid=True)
++ self._user2user(tgt, creds, expected_error=KRB_ERR_TKT_NYV)
++
+ def test_tgs_req_no_requester_sid(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, remove_requester_sid=True)
+diff --git python/samba/tests/krb5/rfc4120_constants.py python/samba/tests/krb5/rfc4120_constants.py
+index 5251e291fde..a9fdc5735dd 100644
+--- python/samba/tests/krb5/rfc4120_constants.py
++++ python/samba/tests/krb5/rfc4120_constants.py
+@@ -76,6 +76,7 @@ KDC_ERR_TGT_REVOKED = 20
+ KDC_ERR_PREAUTH_FAILED = 24
+ KDC_ERR_PREAUTH_REQUIRED = 25
+ KDC_ERR_BAD_INTEGRITY = 31
++KRB_ERR_TKT_NYV = 33
+ KDC_ERR_NOT_US = 35
+ KDC_ERR_BADMATCH = 36
+ KDC_ERR_SKEW = 37
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index cc12499bb50..3aacec00870 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -422,6 +422,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rename
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_invalid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_allowed_denied
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_denied
+--
+2.25.1
+
+
+From 645d30ff371fdf3e16cb1fa69f2e93a848d20bdb Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 12:10:45 +1300
+Subject: [PATCH 30/99] tests/krb5: Add tests for TGS requests with a non-TGT
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 778029c1dc443b87f4ed4b9d2c613d0e6fc45b0d)
+---
+ python/samba/tests/krb5/kdc_tgs_tests.py | 51 ++++++++++++++++++++++++
+ selftest/knownfail_mit_kdc | 2 +
+ 2 files changed, 53 insertions(+)
+
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index f5f091610ac..52297c963e8 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -40,6 +40,7 @@ from samba.tests.krb5.rfc4120_constants import (
+ KDC_ERR_BADMATCH,
+ KDC_ERR_GENERIC,
+ KDC_ERR_MODIFIED,
++ KDC_ERR_NOT_US,
+ KDC_ERR_POLICY,
+ KDC_ERR_C_PRINCIPAL_UNKNOWN,
+ KDC_ERR_S_PRINCIPAL_UNKNOWN,
+@@ -1234,6 +1235,56 @@ class KdcTgsTests(KDCBaseTest):
+ expected_error=(KDC_ERR_GENERIC,
+ KDC_ERR_S_PRINCIPAL_UNKNOWN))
+
++ def test_tgs_service_ticket(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds)
++
++ service_creds = self.get_service_creds()
++ service_ticket = self.get_service_ticket(tgt, service_creds)
++
++ self._run_tgs(service_ticket,
++ expected_error=(KDC_ERR_NOT_US, KDC_ERR_POLICY))
++
++ def test_renew_service_ticket(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds)
++
++ service_creds = self.get_service_creds()
++ service_ticket = self.get_service_ticket(tgt, service_creds)
++
++ service_ticket = self.modified_ticket(
++ service_ticket,
++ modify_fn=self._modify_renewable,
++ checksum_keys=self.get_krbtgt_checksum_key())
++
++ self._renew_tgt(service_ticket,
++ expected_error=KDC_ERR_POLICY)
++
++ def test_validate_service_ticket(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds)
++
++ service_creds = self.get_service_creds()
++ service_ticket = self.get_service_ticket(tgt, service_creds)
++
++ service_ticket = self.modified_ticket(
++ service_ticket,
++ modify_fn=self._modify_invalid,
++ checksum_keys=self.get_krbtgt_checksum_key())
++
++ self._validate_tgt(service_ticket,
++ expected_error=KDC_ERR_POLICY)
++
++ def test_s4u2self_service_ticket(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds)
++
++ service_creds = self.get_service_creds()
++ service_ticket = self.get_service_ticket(tgt, service_creds)
++
++ self._s4u2self(service_ticket, creds,
++ expected_error=(KDC_ERR_NOT_US, KDC_ERR_POLICY))
++
+ def test_user2user_service_ticket(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds)
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 3aacec00870..98e8a34cd5f 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -403,6 +403,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_authdata_no_pac
+@@ -470,6 +471,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_nonexisting
+ #
+--
+2.25.1
+
+
+From 1d616e8e9c0dceabebd1f079fc4d652d6bf2060d Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 12:09:18 +1300
+Subject: [PATCH 31/99] tests/krb5: Add TGS-REQ tests with FAST
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit ec823c2a83c639f1d7c422153a53d366750e5f2a)
+---
+ python/samba/tests/krb5/kdc_tgs_tests.py | 184 ++++++++++++++++++++++-
+ selftest/knownfail_heimdal_kdc | 13 ++
+ selftest/knownfail_mit_kdc | 17 +++
+ 3 files changed, 212 insertions(+), 2 deletions(-)
+
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index 52297c963e8..99a91528fa8 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -32,6 +32,7 @@ os.environ["PYTHONUNBUFFERED"] = "1"
+
+ import samba.tests.krb5.kcrypto as kcrypto
+ from samba.tests.krb5.kdc_base_test import KDCBaseTest
++from samba.tests.krb5.raw_testcase import Krb5EncryptionKey
+ from samba.tests.krb5.rfc4120_constants import (
+ AES256_CTS_HMAC_SHA1_96,
+ ARCFOUR_HMAC_MD5,
+@@ -513,6 +514,11 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._get_tgt(creds)
+ self._user2user(tgt, creds, expected_error=0)
+
++ def test_fast_req(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds)
++ self._fast(tgt, creds, expected_error=0)
++
+ def test_tgs_req_invalid(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, invalid=True)
+@@ -528,6 +534,12 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._get_tgt(creds, invalid=True)
+ self._user2user(tgt, creds, expected_error=KRB_ERR_TKT_NYV)
+
++ def test_fast_req_invalid(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds, invalid=True)
++ self._fast(tgt, creds, expected_error=KRB_ERR_TKT_NYV,
++ expected_sname=self.get_krbtgt_sname())
++
+ def test_tgs_req_no_requester_sid(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, remove_requester_sid=True)
+@@ -583,6 +595,12 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._get_tgt(creds, remove_pac=True)
+ self._user2user(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED)
+
++ def test_fast_no_pac(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds, remove_pac=True)
++ self._fast(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED,
++ expected_sname=self.get_krbtgt_sname())
++
+ # Test making a request with authdata and without a PAC.
+ def test_tgs_authdata_no_pac(self):
+ creds = self._get_creds()
+@@ -613,6 +631,12 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._get_tgt(creds, remove_pac=True, allow_empty_authdata=True)
+ self._user2user(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED)
+
++ def test_fast_authdata_no_pac(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds, remove_pac=True, allow_empty_authdata=True)
++ self._fast(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED,
++ expected_sname=self.get_krbtgt_sname())
++
+ # Test changing the SID in the PAC to that of another account.
+ def test_tgs_sid_mismatch_existing(self):
+ creds = self._get_creds()
+@@ -646,6 +670,14 @@ class KdcTgsTests(KDCBaseTest):
+ self._user2user(tgt, creds,
+ expected_error=KDC_ERR_TGT_REVOKED)
+
++ def test_fast_sid_mismatch_existing(self):
++ creds = self._get_creds()
++ existing_rid = self._get_existing_rid()
++ tgt = self._get_tgt(creds, new_rid=existing_rid)
++ self._fast(tgt, creds,
++ expected_error=KDC_ERR_TGT_REVOKED,
++ expected_sname=self.get_krbtgt_sname())
++
+ def test_requester_sid_mismatch_existing(self):
+ creds = self._get_creds()
+ existing_rid = self._get_existing_rid()
+@@ -702,6 +734,14 @@ class KdcTgsTests(KDCBaseTest):
+ self._user2user(tgt, creds,
+ expected_error=KDC_ERR_TGT_REVOKED)
+
++ def test_fast_sid_mismatch_nonexisting(self):
++ creds = self._get_creds()
++ nonexistent_rid = self._get_non_existent_rid()
++ tgt = self._get_tgt(creds, new_rid=nonexistent_rid)
++ self._fast(tgt, creds,
++ expected_error=KDC_ERR_TGT_REVOKED,
++ expected_sname=self.get_krbtgt_sname())
++
+ def test_requester_sid_mismatch_nonexisting(self):
+ creds = self._get_creds()
+ nonexistent_rid = self._get_non_existent_rid()
+@@ -799,6 +839,16 @@ class KdcTgsTests(KDCBaseTest):
+ self._user2user(tgt, creds,
+ expected_error=KDC_ERR_TGT_REVOKED)
+
++ def test_fast_rodc_sid_mismatch_existing(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++ existing_rid = self._get_existing_rid(replication_allowed=True,
++ revealed_to_rodc=True)
++ tgt = self._get_tgt(creds, from_rodc=True, new_rid=existing_rid)
++ self._fast(tgt, creds,
++ expected_error=KDC_ERR_TGT_REVOKED,
++ expected_sname=self.get_krbtgt_sname())
++
+ def test_tgs_rodc_requester_sid_mismatch_existing(self):
+ creds = self._get_creds(replication_allowed=True,
+ revealed_to_rodc=True)
+@@ -866,6 +916,15 @@ class KdcTgsTests(KDCBaseTest):
+ self._user2user(tgt, creds,
+ expected_error=KDC_ERR_TGT_REVOKED)
+
++ def test_fast_rodc_sid_mismatch_nonexisting(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++ nonexistent_rid = self._get_non_existent_rid()
++ tgt = self._get_tgt(creds, from_rodc=True, new_rid=nonexistent_rid)
++ self._fast(tgt, creds,
++ expected_error=KDC_ERR_TGT_REVOKED,
++ expected_sname=self.get_krbtgt_sname())
++
+ def test_tgs_rodc_requester_sid_mismatch_nonexisting(self):
+ creds = self._get_creds(replication_allowed=True,
+ revealed_to_rodc=True)
+@@ -955,6 +1014,14 @@ class KdcTgsTests(KDCBaseTest):
+ self._remove_rodc_partial_secrets()
+ self._user2user(tgt, creds, expected_error=KDC_ERR_POLICY)
+
++ def test_fast_rodc_no_partial_secrets(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++ tgt = self._get_tgt(creds, from_rodc=True)
++ self._remove_rodc_partial_secrets()
++ self._fast(tgt, creds, expected_error=KDC_ERR_POLICY,
++ expected_sname=self.get_krbtgt_sname())
++
+ # Test with an RODC-issued ticket where the RODC account does not have an
+ # msDS-KrbTgtLink.
+ def test_tgs_rodc_no_krbtgt_link(self):
+@@ -992,6 +1059,14 @@ class KdcTgsTests(KDCBaseTest):
+ self._remove_rodc_krbtgt_link()
+ self._user2user(tgt, creds, expected_error=KDC_ERR_POLICY)
+
++ def test_fast_rodc_no_krbtgt_link(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++ tgt = self._get_tgt(creds, from_rodc=True)
++ self._remove_rodc_krbtgt_link()
++ self._fast(tgt, creds, expected_error=KDC_ERR_POLICY,
++ expected_sname=self.get_krbtgt_sname())
++
+ # Test with an RODC-issued ticket where the client is not allowed to
+ # replicate to the RODC.
+ def test_tgs_rodc_not_allowed(self):
+@@ -1019,6 +1094,12 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._get_tgt(creds, from_rodc=True)
+ self._user2user(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED)
+
++ def test_fast_rodc_not_allowed(self):
++ creds = self._get_creds(revealed_to_rodc=True)
++ tgt = self._get_tgt(creds, from_rodc=True)
++ self._fast(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED,
++ expected_sname=self.get_krbtgt_sname())
++
+ # Test with an RODC-issued ticket where the client is denied from
+ # replicating to the RODC.
+ def test_tgs_rodc_denied(self):
+@@ -1051,6 +1132,13 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._get_tgt(creds, from_rodc=True)
+ self._user2user(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED)
+
++ def test_fast_rodc_denied(self):
++ creds = self._get_creds(replication_denied=True,
++ revealed_to_rodc=True)
++ tgt = self._get_tgt(creds, from_rodc=True)
++ self._fast(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED,
++ expected_sname=self.get_krbtgt_sname())
++
+ # Test with an RODC-issued ticket where the client is both allowed and
+ # denied replicating to the RODC.
+ def test_tgs_rodc_allowed_denied(self):
+@@ -1088,6 +1176,14 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._get_tgt(creds, from_rodc=True)
+ self._user2user(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED)
+
++ def test_fast_rodc_allowed_denied(self):
++ creds = self._get_creds(replication_allowed=True,
++ replication_denied=True,
++ revealed_to_rodc=True)
++ tgt = self._get_tgt(creds, from_rodc=True)
++ self._fast(tgt, creds, expected_error=KDC_ERR_TGT_REVOKED,
++ expected_sname=self.get_krbtgt_sname())
++
+ # Test user-to-user with incorrect service principal names.
+ def test_user2user_matching_sname_host(self):
+ creds = self._get_creds()
+@@ -1295,6 +1391,17 @@ class KdcTgsTests(KDCBaseTest):
+ self._user2user(service_ticket, creds,
+ expected_error=(KDC_ERR_MODIFIED, KDC_ERR_POLICY))
+
++ # Expected to fail against Windows, which does not produce a policy error.
++ def test_fast_service_ticket(self):
++ creds = self._get_creds()
++ tgt = self._get_tgt(creds)
++
++ service_creds = self.get_service_creds()
++ service_ticket = self.get_service_ticket(tgt, service_creds)
++
++ self._fast(service_ticket, creds,
++ expected_error=KDC_ERR_POLICY)
++
+ def test_pac_attrs_none(self):
+ creds = self._get_creds()
+ self.get_tgt(creds, pac_request=None,
+@@ -1792,6 +1899,34 @@ class KdcTgsTests(KDCBaseTest):
+ pac = self.get_ticket_pac(ticket)
+ self.assertIsNotNone(pac)
+
++ def test_fast_pac_request_none(self):
++ creds = self._get_creds()
++ tgt = self.get_tgt(creds, pac_request=None)
++
++ ticket = self._fast(tgt, creds, expected_error=0, expect_pac=True)
++
++ pac = self.get_ticket_pac(ticket)
++ self.assertIsNotNone(pac)
++
++ def test_fast_pac_request_false(self):
++ creds = self._get_creds()
++ tgt = self.get_tgt(creds, pac_request=False)
++
++ ticket = self._fast(tgt, creds, expected_error=0,
++ expect_pac=True)
++
++ pac = self.get_ticket_pac(ticket, expect_pac=True)
++ self.assertIsNotNone(pac)
++
++ def test_fast_pac_request_true(self):
++ creds = self._get_creds()
++ tgt = self.get_tgt(creds, pac_request=True)
++
++ ticket = self._fast(tgt, creds, expected_error=0, expect_pac=True)
++
++ pac = self.get_ticket_pac(ticket)
++ self.assertIsNotNone(pac)
++
+ def test_tgs_rodc_pac_request_none(self):
+ creds = self._get_creds(replication_allowed=True,
+ revealed_to_rodc=True)
+@@ -2192,13 +2327,28 @@ class KdcTgsTests(KDCBaseTest):
+ srealm=srealm,
+ expect_pac=expect_pac)
+
++ def _fast(self, armor_tgt, armor_tgt_creds, expected_error,
++ expected_sname=None, expect_pac=True):
++ user_creds = self._get_mach_creds()
++ user_tgt = self.get_tgt(user_creds)
++
++ target_creds = self.get_service_creds()
++
++ return self._tgs_req(user_tgt, expected_error, target_creds,
++ armor_tgt=armor_tgt,
++ expected_sname=expected_sname,
++ expect_pac=expect_pac)
++
+ def _tgs_req(self, tgt, expected_error, target_creds,
++ armor_tgt=None,
+ kdc_options='0',
+ expected_cname=None,
++ expected_sname=None,
+ additional_ticket=None,
+ generate_padata_fn=None,
+ sname=None,
+ srealm=None,
++ use_fast=False,
+ expect_claims=True,
+ expect_pac=True,
+ expect_pac_attrs=None,
+@@ -2214,7 +2364,8 @@ class KdcTgsTests(KDCBaseTest):
+
+ if sname is False:
+ sname = None
+- expected_sname = self.get_krbtgt_sname()
++ if expected_sname is None:
++ expected_sname = self.get_krbtgt_sname()
+ else:
+ if sname is None:
+ target_name = target_creds.get_username()
+@@ -2229,7 +2380,8 @@ class KdcTgsTests(KDCBaseTest):
+ name_type=NT_PRINCIPAL,
+ names=['host', target_name])
+
+- expected_sname = sname
++ if expected_sname is None:
++ expected_sname = sname
+
+ if additional_ticket is not None:
+ additional_tickets = [additional_ticket.ticket]
+@@ -2241,6 +2393,28 @@ class KdcTgsTests(KDCBaseTest):
+
+ subkey = self.RandomKey(tgt.session_key.etype)
+
++ if armor_tgt is not None:
++ armor_subkey = self.RandomKey(subkey.etype)
++ explicit_armor_key = self.generate_armor_key(armor_subkey,
++ armor_tgt.session_key)
++ armor_key = kcrypto.cf2(explicit_armor_key.key,
++ subkey.key,
++ b'explicitarmor',
++ b'tgsarmor')
++ armor_key = Krb5EncryptionKey(armor_key, None)
++
++ generate_fast_fn = self.generate_simple_fast
++ generate_fast_armor_fn = self.generate_ap_req
++
++ pac_options = '1' # claims support
++ else:
++ armor_subkey = None
++ armor_key = None
++ generate_fast_fn = None
++ generate_fast_armor_fn = None
++
++ pac_options = None
++
+ etypes = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
+
+ if expected_error:
+@@ -2260,12 +2434,18 @@ class KdcTgsTests(KDCBaseTest):
+ expected_sname=expected_sname,
+ ticket_decryption_key=decryption_key,
+ generate_padata_fn=generate_padata_fn,
++ generate_fast_fn=generate_fast_fn,
++ generate_fast_armor_fn=generate_fast_armor_fn,
+ check_error_fn=check_error_fn,
+ check_rep_fn=check_rep_fn,
+ check_kdc_private_fn=self.generic_check_kdc_private,
+ expected_error_mode=expected_error,
+ expected_status=expected_status,
+ tgt=tgt,
++ armor_key=armor_key,
++ armor_tgt=armor_tgt,
++ armor_subkey=armor_subkey,
++ pac_options=pac_options,
+ authenticator_subkey=subkey,
+ kdc_options=kdc_options,
+ expect_edata=expect_edata,
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 1b7e159c381..61de06659be 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -274,6 +274,19 @@
+ #
+ # KDC TGS tests
+ #
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_authdata_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_req_invalid
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_allowed_denied
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_denied
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_no_krbtgt_link
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_no_partial_secrets
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_not_allowed
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 98e8a34cd5f..3e19ee6c8b9 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -390,6 +390,23 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ #
+ # KDC TGT tests
+ #
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_authdata_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_pac_request_false
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_pac_request_none
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_pac_request_true
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_req
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_req_invalid
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_allowed_denied
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_denied
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_no_krbtgt_link
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_no_partial_secrets
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_not_allowed
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_rodc_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_authdata_no_pac
+--
+2.25.1
+
+
+From 5375e2b99cd5fd9e40d6d5f94eb7d46f366f525e Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 12:37:08 +1300
+Subject: [PATCH 32/99] tests/krb5: Align PAC buffer checking to more closely
+ match Windows with PacRequestorEnforcement=2
+
+We set EXPECT_EXTRA_PAC_BUFFERS to 0 for the moment. This signifies that
+these checks are currently not enforced, which avoids a lot of test
+failures.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit ebc9137cee94dee9dcf0e47d5bc0dc83de7aaaa1)
+
+[jsutton@samba.org Fixed conflicts]
+---
+ python/samba/tests/krb5/kdc_tgs_tests.py | 121 ++++++++++++++++-------
+ python/samba/tests/krb5/raw_testcase.py | 39 ++++++--
+ selftest/knownfail_heimdal_kdc | 9 ++
+ selftest/knownfail_mit_kdc | 6 ++
+ source4/selftest/tests.py | 58 +++++++----
+ 5 files changed, 168 insertions(+), 65 deletions(-)
+
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index 99a91528fa8..f14439a4ab5 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -497,12 +497,18 @@ class KdcTgsTests(KDCBaseTest):
+ def test_renew_req(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, renewable=True)
+- self._renew_tgt(tgt, expected_error=0)
++ self._renew_tgt(tgt, expected_error=0,
++ expect_pac_attrs=True,
++ expect_pac_attrs_pac_request=True,
++ expect_requester_sid=True)
+
+ def test_validate_req(self):
+ creds = self._get_creds()
+ tgt = self._get_tgt(creds, invalid=True)
+- self._validate_tgt(tgt, expected_error=0)
++ self._validate_tgt(tgt, expected_error=0,
++ expect_pac_attrs=True,
++ expect_pac_attrs_pac_request=True,
++ expect_requester_sid=True)
+
+ def test_s4u2self_req(self):
+ creds = self._get_creds()
+@@ -774,13 +780,17 @@ class KdcTgsTests(KDCBaseTest):
+ creds = self._get_creds(replication_allowed=True,
+ revealed_to_rodc=True)
+ tgt = self._get_tgt(creds, renewable=True, from_rodc=True)
+- self._renew_tgt(tgt, expected_error=0)
++ self._renew_tgt(tgt, expected_error=0,
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
+
+ def test_validate_rodc_revealed(self):
+ creds = self._get_creds(replication_allowed=True,
+ revealed_to_rodc=True)
+ tgt = self._get_tgt(creds, invalid=True, from_rodc=True)
+- self._validate_tgt(tgt, expected_error=0)
++ self._validate_tgt(tgt, expected_error=0,
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
+
+ def test_s4u2self_rodc_revealed(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -1434,7 +1444,8 @@ class KdcTgsTests(KDCBaseTest):
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+ expect_pac_attrs=True,
+- expect_pac_attrs_pac_request=None)
++ expect_pac_attrs_pac_request=None,
++ expect_requester_sid=True)
+
+ def test_pac_attrs_renew_false(self):
+ creds = self._get_creds()
+@@ -1447,7 +1458,8 @@ class KdcTgsTests(KDCBaseTest):
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+ expect_pac_attrs=True,
+- expect_pac_attrs_pac_request=False)
++ expect_pac_attrs_pac_request=False,
++ expect_requester_sid=True)
+
+ def test_pac_attrs_renew_true(self):
+ creds = self._get_creds()
+@@ -1460,7 +1472,8 @@ class KdcTgsTests(KDCBaseTest):
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+ expect_pac_attrs=True,
+- expect_pac_attrs_pac_request=True)
++ expect_pac_attrs_pac_request=True,
++ expect_requester_sid=True)
+
+ def test_pac_attrs_rodc_renew_none(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -1473,8 +1486,8 @@ class KdcTgsTests(KDCBaseTest):
+
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+- expect_pac_attrs=True,
+- expect_pac_attrs_pac_request=None)
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
+
+ def test_pac_attrs_rodc_renew_false(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -1487,8 +1500,8 @@ class KdcTgsTests(KDCBaseTest):
+
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+- expect_pac_attrs=True,
+- expect_pac_attrs_pac_request=False)
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
+
+ def test_pac_attrs_rodc_renew_true(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -1501,8 +1514,8 @@ class KdcTgsTests(KDCBaseTest):
+
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+- expect_pac_attrs=True,
+- expect_pac_attrs_pac_request=True)
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
+
+ def test_pac_attrs_missing_renew_none(self):
+ creds = self._get_creds()
+@@ -1515,7 +1528,8 @@ class KdcTgsTests(KDCBaseTest):
+
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+- expect_pac_attrs=False)
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
+
+ def test_pac_attrs_missing_renew_false(self):
+ creds = self._get_creds()
+@@ -1528,7 +1542,8 @@ class KdcTgsTests(KDCBaseTest):
+
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+- expect_pac_attrs=False)
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
+
+ def test_pac_attrs_missing_renew_true(self):
+ creds = self._get_creds()
+@@ -1541,7 +1556,8 @@ class KdcTgsTests(KDCBaseTest):
+
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+- expect_pac_attrs=False)
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
+
+ def test_pac_attrs_missing_rodc_renew_none(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -1555,7 +1571,8 @@ class KdcTgsTests(KDCBaseTest):
+
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+- expect_pac_attrs=False)
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
+
+ def test_pac_attrs_missing_rodc_renew_false(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -1569,7 +1586,8 @@ class KdcTgsTests(KDCBaseTest):
+
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+- expect_pac_attrs=False)
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
+
+ def test_pac_attrs_missing_rodc_renew_true(self):
+ creds = self._get_creds(replication_allowed=True,
+@@ -1583,7 +1601,8 @@ class KdcTgsTests(KDCBaseTest):
+
+ self._renew_tgt(tgt, expected_error=0,
+ expect_pac=True,
+- expect_pac_attrs=False)
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
+
+ def test_tgs_pac_attrs_none(self):
+ creds = self._get_creds()
+@@ -1593,8 +1612,7 @@ class KdcTgsTests(KDCBaseTest):
+ expect_pac_attrs_pac_request=None)
+
+ self._run_tgs(tgt, expected_error=0, expect_pac=True,
+- expect_pac_attrs=True,
+- expect_pac_attrs_pac_request=None)
++ expect_pac_attrs=False)
+
+ def test_tgs_pac_attrs_false(self):
+ creds = self._get_creds()
+@@ -1603,7 +1621,8 @@ class KdcTgsTests(KDCBaseTest):
+ expect_pac_attrs=True,
+ expect_pac_attrs_pac_request=False)
+
+- self._run_tgs(tgt, expected_error=0, expect_pac=False)
++ self._run_tgs(tgt, expected_error=0, expect_pac=False,
++ expect_pac_attrs=False)
+
+ def test_tgs_pac_attrs_true(self):
+ creds = self._get_creds()
+@@ -1613,8 +1632,7 @@ class KdcTgsTests(KDCBaseTest):
+ expect_pac_attrs_pac_request=True)
+
+ self._run_tgs(tgt, expected_error=0, expect_pac=True,
+- expect_pac_attrs=True,
+- expect_pac_attrs_pac_request=True)
++ expect_pac_attrs=False)
+
+ def test_as_requester_sid(self):
+ creds = self._get_creds()
+@@ -1639,8 +1657,7 @@ class KdcTgsTests(KDCBaseTest):
+ expect_requester_sid=True)
+
+ self._run_tgs(tgt, expected_error=0, expect_pac=True,
+- expected_sid=sid,
+- expect_requester_sid=True)
++ expect_requester_sid=False)
+
+ def test_tgs_requester_sid_renew(self):
+ creds = self._get_creds()
+@@ -1655,6 +1672,8 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._modify_tgt(tgt, renewable=True)
+
+ self._renew_tgt(tgt, expected_error=0, expect_pac=True,
++ expect_pac_attrs=True,
++ expect_pac_attrs_pac_request=None,
+ expected_sid=sid,
+ expect_requester_sid=True)
+
+@@ -1672,6 +1691,7 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self._modify_tgt(tgt, from_rodc=True, renewable=True)
+
+ self._renew_tgt(tgt, expected_error=0, expect_pac=True,
++ expect_pac_attrs=False,
+ expected_sid=sid,
+ expect_requester_sid=True)
+
+@@ -1738,7 +1758,10 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self.get_tgt(creds, pac_request=None)
+ tgt = self._modify_tgt(tgt, renewable=True)
+
+- tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None)
++ tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=True,
++ expect_pac_attrs_pac_request=None,
++ expect_requester_sid=True)
+
+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
+
+@@ -1750,7 +1773,10 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None)
+ tgt = self._modify_tgt(tgt, renewable=True)
+
+- tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None)
++ tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=True,
++ expect_pac_attrs_pac_request=False,
++ expect_requester_sid=True)
+
+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=False)
+
+@@ -1762,7 +1788,10 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self.get_tgt(creds, pac_request=True)
+ tgt = self._modify_tgt(tgt, renewable=True)
+
+- tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None)
++ tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=True,
++ expect_pac_attrs_pac_request=True,
++ expect_requester_sid=True)
+
+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
+
+@@ -1774,7 +1803,10 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self.get_tgt(creds, pac_request=None)
+ tgt = self._modify_tgt(tgt, invalid=True)
+
+- tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None)
++ tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=True,
++ expect_pac_attrs_pac_request=None,
++ expect_requester_sid=True)
+
+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
+
+@@ -1786,7 +1818,10 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None)
+ tgt = self._modify_tgt(tgt, invalid=True)
+
+- tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None)
++ tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=True,
++ expect_pac_attrs_pac_request=False,
++ expect_requester_sid=True)
+
+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=False)
+
+@@ -1798,7 +1833,10 @@ class KdcTgsTests(KDCBaseTest):
+ tgt = self.get_tgt(creds, pac_request=True)
+ tgt = self._modify_tgt(tgt, invalid=True)
+
+- tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None)
++ tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=True,
++ expect_pac_attrs_pac_request=True,
++ expect_requester_sid=True)
+
+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
+
+@@ -1946,7 +1984,7 @@ class KdcTgsTests(KDCBaseTest):
+
+ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
+
+- pac = self.get_ticket_pac(ticket, expect_pac=False)
++ pac = self.get_ticket_pac(ticket)
+ self.assertIsNotNone(pac)
+
+ def test_tgs_rodc_pac_request_true(self):
+@@ -2279,12 +2317,21 @@ class KdcTgsTests(KDCBaseTest):
+ expect_requester_sid=expect_requester_sid,
+ expected_sid=expected_sid)
+
+- def _validate_tgt(self, tgt, expected_error, expect_pac=True):
++ def _validate_tgt(self, tgt, expected_error, expect_pac=True,
++ expect_pac_attrs=None,
++ expect_pac_attrs_pac_request=None,
++ expect_requester_sid=None,
++ expected_sid=None):
+ krbtgt_creds = self.get_krbtgt_creds()
+ kdc_options = str(krb5_asn1.KDCOptions('validate'))
+- return self._tgs_req(tgt, expected_error, krbtgt_creds,
+- kdc_options=kdc_options,
+- expect_pac=expect_pac)
++ return self._tgs_req(
++ tgt, expected_error, krbtgt_creds,
++ kdc_options=kdc_options,
++ expect_pac=expect_pac,
++ expect_pac_attrs=expect_pac_attrs,
++ expect_pac_attrs_pac_request=expect_pac_attrs_pac_request,
++ expect_requester_sid=expect_requester_sid,
++ expected_sid=expected_sid)
+
+ def _s4u2self(self, tgt, tgt_creds, expected_error, expect_pac=True,
+ expect_edata=False, expected_status=None):
+diff --git python/samba/tests/krb5/raw_testcase.py python/samba/tests/krb5/raw_testcase.py
+index da3f69c79c6..14e655313fc 100644
+--- python/samba/tests/krb5/raw_testcase.py
++++ python/samba/tests/krb5/raw_testcase.py
+@@ -602,6 +602,13 @@ class RawKerberosTest(TestCaseInTempDir):
+ expect_pac = '1'
+ cls.expect_pac = bool(int(expect_pac))
+
++ expect_extra_pac_buffers = samba.tests.env_get_var_value(
++ 'EXPECT_EXTRA_PAC_BUFFERS',
++ allow_missing=True)
++ if expect_extra_pac_buffers is None:
++ expect_extra_pac_buffers = '1'
++ cls.expect_extra_pac_buffers = bool(int(expect_extra_pac_buffers))
++
+ def setUp(self):
+ super().setUp()
+ self.do_asn1_print = False
+@@ -2624,17 +2631,34 @@ class RawKerberosTest(TestCaseInTempDir):
+ if not self.tkt_sig_support:
+ require_strict.add(krb5pac.PAC_TYPE_TICKET_CHECKSUM)
+
++ expect_extra_pac_buffers = rep_msg_type == KRB_AS_REP
++
+ expect_pac_attrs = kdc_exchange_dict['expect_pac_attrs']
++
++ if expect_pac_attrs:
++ expect_pac_attrs_pac_request = kdc_exchange_dict[
++ 'expect_pac_attrs_pac_request']
++ else:
++ expect_pac_attrs_pac_request = kdc_exchange_dict[
++ 'pac_request']
++
++ if expect_pac_attrs is None:
++ if self.expect_extra_pac_buffers:
++ expect_pac_attrs = expect_extra_pac_buffers
++ else:
++ require_strict.add(krb5pac.PAC_TYPE_ATTRIBUTES_INFO)
+ if expect_pac_attrs:
+ expected_types.append(krb5pac.PAC_TYPE_ATTRIBUTES_INFO)
+- elif expect_pac_attrs is None:
+- require_strict.add(krb5pac.PAC_TYPE_ATTRIBUTES_INFO)
+
+ expect_requester_sid = kdc_exchange_dict['expect_requester_sid']
++
++ if expect_requester_sid is None:
++ if self.expect_extra_pac_buffers:
++ expect_requester_sid = expect_extra_pac_buffers
++ else:
++ require_strict.add(krb5pac.PAC_TYPE_REQUESTER_SID)
+ if expect_requester_sid:
+ expected_types.append(krb5pac.PAC_TYPE_REQUESTER_SID)
+- elif expect_requester_sid is None:
+- require_strict.add(krb5pac.PAC_TYPE_REQUESTER_SID)
+
+ buffer_types = [pac_buffer.type
+ for pac_buffer in pac.buffers]
+@@ -2722,9 +2746,6 @@ class RawKerberosTest(TestCaseInTempDir):
+ requested_pac = bool(flags & 1)
+ given_pac = bool(flags & 2)
+
+- expect_pac_attrs_pac_request = kdc_exchange_dict[
+- 'expect_pac_attrs_pac_request']
+-
+ self.assertEqual(expect_pac_attrs_pac_request is True,
+ requested_pac)
+ self.assertEqual(expect_pac_attrs_pac_request is None,
+@@ -2734,8 +2755,8 @@ class RawKerberosTest(TestCaseInTempDir):
+ and expect_requester_sid):
+ requester_sid = pac_buffer.info.sid
+
+- self.assertIsNotNone(expected_sid)
+- self.assertEqual(expected_sid, str(requester_sid))
++ if expected_sid is not None:
++ self.assertEqual(expected_sid, str(requester_sid))
+
+ def generic_check_kdc_error(self,
+ kdc_exchange_dict,
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 61de06659be..294e06027b1 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -289,11 +289,15 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_false
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_none
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_client_no_auth_data_required
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_service_no_auth_data_required
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing
+@@ -309,10 +313,14 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_none
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_no_requester_sid
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid(?!_)
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_renew
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_false
+@@ -332,6 +340,7 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname_krbtgt
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 3e19ee6c8b9..6c74657e87d 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -411,6 +411,9 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_pac_request_none
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_pac_request_true
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_req
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_allowed_denied
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_denied
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_no_krbtgt_link
+@@ -479,6 +482,9 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_srealm
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_pac_request_none
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_pac_request_true
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_req
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_allowed_denied
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_denied
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_no_krbtgt_link
+
+From 3fdfbd08b9460fb486f100d7091984f41ebd9429 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 13:10:52 +1300
+Subject: [PATCH 33/99] tests/krb5: Add tests for validation with requester SID
+ PAC buffer
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit ca80c47406e0f2b6fac2c55229306e21ccef9745)
+---
+ python/samba/tests/krb5/kdc_tgs_tests.py | 67 ++++++++++++++++++++++++
+ selftest/knownfail_heimdal_kdc | 3 ++
+ selftest/knownfail_mit_kdc | 4 ++
+ 3 files changed, 74 insertions(+)
+
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index f14439a4ab5..50079a1710c 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -1726,6 +1726,73 @@ class KdcTgsTests(KDCBaseTest):
+
+ self._renew_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
+
++ def test_tgs_requester_sid_validate(self):
++ creds = self._get_creds()
++
++ samdb = self.get_samdb()
++ sid = self.get_objectSid(samdb, creds.get_dn())
++
++ tgt = self.get_tgt(creds, pac_request=None,
++ expect_pac=True,
++ expected_sid=sid,
++ expect_requester_sid=True)
++ tgt = self._modify_tgt(tgt, invalid=True)
++
++ self._validate_tgt(tgt, expected_error=0, expect_pac=True,
++ expect_pac_attrs=True,
++ expect_pac_attrs_pac_request=None,
++ expected_sid=sid,
++ expect_requester_sid=True)
++
++ def test_tgs_requester_sid_rodc_validate(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++
++ samdb = self.get_samdb()
++ sid = self.get_objectSid(samdb, creds.get_dn())
++
++ tgt = self.get_tgt(creds, pac_request=None,
++ expect_pac=True,
++ expected_sid=sid,
++ expect_requester_sid=True)
++ tgt = self._modify_tgt(tgt, from_rodc=True, invalid=True)
++
++ self._validate_tgt(tgt, expected_error=0, expect_pac=True,
++ expect_pac_attrs=False,
++ expected_sid=sid,
++ expect_requester_sid=True)
++
++ def test_tgs_requester_sid_missing_validate(self):
++ creds = self._get_creds()
++
++ samdb = self.get_samdb()
++ sid = self.get_objectSid(samdb, creds.get_dn())
++
++ tgt = self.get_tgt(creds, pac_request=None,
++ expect_pac=True,
++ expected_sid=sid,
++ expect_requester_sid=True)
++ tgt = self._modify_tgt(tgt, invalid=True,
++ remove_requester_sid=True)
++
++ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
++
++ def test_tgs_requester_sid_missing_rodc_validate(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++
++ samdb = self.get_samdb()
++ sid = self.get_objectSid(samdb, creds.get_dn())
++
++ tgt = self.get_tgt(creds, pac_request=None,
++ expect_pac=True,
++ expected_sid=sid,
++ expect_requester_sid=True)
++ tgt = self._modify_tgt(tgt, from_rodc=True, invalid=True,
++ remove_requester_sid=True)
++
++ self._validate_tgt(tgt, expected_error=KDC_ERR_TGT_REVOKED)
++
+ def test_tgs_pac_request_none(self):
+ creds = self._get_creds()
+ tgt = self.get_tgt(creds, pac_request=None)
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 294e06027b1..f7c5feda872 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -320,7 +320,10 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid(?!_)
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_renew
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_validate
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_validate
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_validate
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_false
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 6c74657e87d..ff287e6cd9d 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -546,8 +546,12 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_renew
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_validate
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_validate
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_validate
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_validate
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_sid_mismatch_existing
+--
+2.25.1
+
+
+From 69233dd323b1ce715387e6015542ed234d909295 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 15:32:32 +1300
+Subject: [PATCH 34/99] tests/krb5: Add comments for tests that fail against
+ Windows
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 749349efab9b401d33a4fc286473a924364a41c9)
+---
+ python/samba/tests/krb5/kdc_tgs_tests.py | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index 50079a1710c..ecc38538e61 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -792,6 +792,8 @@ class KdcTgsTests(KDCBaseTest):
+ expect_pac_attrs=False,
+ expect_requester_sid=True)
+
++ # This test fails on Windows, which gives KDC_ERR_C_PRINCIPAL_UNKNOWN when
++ # attempting to use S4U2Self with a TGT from an RODC.
+ def test_s4u2self_rodc_revealed(self):
+ creds = self._get_creds(replication_allowed=True,
+ revealed_to_rodc=True)
+@@ -2370,6 +2372,8 @@ class KdcTgsTests(KDCBaseTest):
+ expect_requester_sid=expect_requester_sid,
+ expected_sid=expected_sid)
+
++ # These tests fail against Windows, which does not implement ticket
++ # renewal.
+ def _renew_tgt(self, tgt, expected_error, expect_pac=True,
+ expect_pac_attrs=None, expect_pac_attrs_pac_request=None,
+ expect_requester_sid=None, expected_sid=None):
+@@ -2384,6 +2388,8 @@ class KdcTgsTests(KDCBaseTest):
+ expect_requester_sid=expect_requester_sid,
+ expected_sid=expected_sid)
+
++ # These tests fail against Windows, which does not implement ticket
++ # validation.
+ def _validate_tgt(self, tgt, expected_error, expect_pac=True,
+ expect_pac_attrs=None,
+ expect_pac_attrs_pac_request=None,
+--
+2.25.1
+
+
+From 6dbed53756f6bac8f63847644b3e9cbb7b6181b0 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 18 Nov 2021 13:14:51 +1300
+Subject: [PATCH 35/99] heimdal:kdc: Fix error message for user-to-user
+
+We were checking the wrong variable to see whether a PAC was found or not.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 11fb9476ad3c09415d12b3cdf7934c293cbefcb2)
+---
+ source4/heimdal/kdc/krb5tgs.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git source4/heimdal/kdc/krb5tgs.c source4/heimdal/kdc/krb5tgs.c
+index fb2ef8230c9..cde68b41714 100644
+--- source4/heimdal/kdc/krb5tgs.c
++++ source4/heimdal/kdc/krb5tgs.c
+@@ -1629,7 +1629,7 @@ server_lookup:
+ ret = KRB5KDC_ERR_BADOPTION;
+ kdc_log(context, config, 0,
+ "Ticket not signed with PAC; user-to-user failed (%s).",
+- mspac ? "Ticket unsigned" : "No PAC");
++ user2user_pac ? "Ticket unsigned" : "No PAC");
+ goto out;
+ }
+
+--
+2.25.1
+
+
+From 33d5e5ad3a06ca6a1a62e64d323580ca60f068b8 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 18 Nov 2021 16:22:34 +1300
+Subject: [PATCH 36/99] s4:torture: Fix typo
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 9cfb88ba04818b5e9cec3c96422e8e4a3080d490)
+---
+ source4/torture/krb5/kdc-canon-heimdal.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git source4/torture/krb5/kdc-canon-heimdal.c source4/torture/krb5/kdc-canon-heimdal.c
+index cd47182c0ef..059078a4ffb 100644
+--- source4/torture/krb5/kdc-canon-heimdal.c
++++ source4/torture/krb5/kdc-canon-heimdal.c
+@@ -262,7 +262,7 @@ static bool torture_krb5_pre_send_as_req_test(struct torture_krb5_context *test_
+ KRB5_NT_PRINCIPAL,
+ "krb5 libs unexpectedly "
+ "did not set principal "
+- "as NT_SRV_HST!");
++ "as NT_PRINCIPAL!");
+ } else {
+ torture_assert_int_equal(test_context->tctx,
+ test_context->as_req.req_body.cname->name_type,
+--
+2.25.1
+
+
+From 02ceb9be33dca0e3a885fd7d85b1199f76e04670 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 20:41:34 +1300
+Subject: [PATCH 37/99] heimdal:kdc: Adjust no-PAC error code to match Windows
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit f7a2fef8f49a86f63c3dc2f6a2d7d979fb53238a)
+---
+ selftest/knownfail_heimdal_kdc | 19 -------------------
+ source4/heimdal/kdc/krb5tgs.c | 2 +-
+ 2 files changed, 1 insertion(+), 20 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index f7c5feda872..9ff85fe18fc 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -233,21 +233,15 @@
+ # S4U tests
+ #
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_bronze_bit_rbcd_old_checksum
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_client_pac(?!_no_auth_data_required)
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_service_pac\(.*\)$
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_service_pac_no_auth_data_required
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_existing_delegation_info
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_missing_client_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_a
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_client_pac_b
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_service_pac
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_service_pac_no_auth_data_required
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_unkeyed_client_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_unkeyed_service_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_client_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_service_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_no_pac
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed
+ #
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_auth_data_required
+@@ -292,11 +286,6 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_false
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_true
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_client_no_auth_data_required
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_remove_pac_service_no_auth_data_required
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_authdata_no_pac
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting
+@@ -304,15 +293,11 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_authdata_no_pac
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_false
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_authdata_no_pac
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid
+@@ -333,16 +318,12 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname_krbtgt
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_authdata_no_pac
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting
+diff --git source4/heimdal/kdc/krb5tgs.c source4/heimdal/kdc/krb5tgs.c
+index cde68b41714..6c5c51aa448 100644
+--- source4/heimdal/kdc/krb5tgs.c
++++ source4/heimdal/kdc/krb5tgs.c
+@@ -78,7 +78,7 @@ check_PAC(krb5_context context,
+ return ret;
+
+ if (pac == NULL)
+- return KRB5KDC_ERR_BADOPTION;
++ return KRB5KDC_ERR_TGT_REVOKED;
+
+ /* Verify the server signature. */
+ ret = krb5_pac_verify(context, pac, tkt->authtime, client_principal,
+--
+2.25.1
+
+
+From 5556f97c782c9be9af47c76f2432bb8480bc0622 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 20:41:45 +1300
+Subject: [PATCH 38/99] kdc: Adjust SID mismatch error code to match Windows
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit d5d22bf84a71492342287e54b555c9f024e7e71c)
+---
+ selftest/knownfail_heimdal_kdc | 35 ----------------------------------
+ selftest/knownfail_mit_kdc | 8 --------
+ source4/kdc/pac-glue.c | 6 +-----
+ 3 files changed, 1 insertion(+), 48 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 9ff85fe18fc..bc644587319 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -259,13 +259,6 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_not_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_not_revealed
+ #
+-# Alias tests
+-#
+-^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_delete
+-^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_create_alias_rename
+-^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_delete
+-^samba.tests.krb5.alias_tests.samba.tests.krb5.alias_tests.AliasTests.test_dc_alias_rename
+-#
+ # KDC TGS tests
+ #
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_authdata_no_pac
+@@ -281,23 +274,11 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_false
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_revealed
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_false
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid
+@@ -309,23 +290,7 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_validate
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_validate
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_false
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_rodc_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname_krbtgt
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_nonexisting
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index ff287e6cd9d..c6dc1285837 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -407,8 +407,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_only_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_pac_request_none
+@@ -424,8 +422,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_service_ticket
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_req
+@@ -454,8 +450,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_no_sname
+@@ -495,8 +489,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_service_ticket
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_sid_mismatch_nonexisting
+ #
+ # PAC attributes tests
+ #
+diff --git source4/kdc/pac-glue.c source4/kdc/pac-glue.c
+index e0e483662c0..2a96a683cd9 100644
+--- source4/kdc/pac-glue.c
++++ source4/kdc/pac-glue.c
+@@ -1237,11 +1237,7 @@ krb5_error_code samba_kdc_validate_pac_blob(
+ "PAC[%s] != CLI[%s]\n",
+ dom_sid_str_buf(&pac_sid, &buf1),
+ dom_sid_str_buf(client_sid, &buf2));
+-#if defined(KRB5KDC_ERR_CLIENT_NAME_MISMATCH) /* MIT */
+- code = KRB5KDC_ERR_CLIENT_NAME_MISMATCH;
+-#else /* Heimdal (where this is an enum) */
+- code = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
+-#endif
++ code = KRB5KDC_ERR_TGT_REVOKED;
+ goto out;
+ }
+
+--
+2.25.1
+
+
+From c62a2b7a218e2c4bdbd476a055049e78b8c0f4ce Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 25 Nov 2021 10:05:17 +1300
+Subject: [PATCH 39/99] tests/krb5: Add test for S4U2Self with wrong sname
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit bac5f75059450898937be891e863826e1350b62c)
+---
+ python/samba/tests/krb5/s4u_tests.py | 32 +++++++++++++++++++++++++++-
+ selftest/knownfail_heimdal_kdc | 1 +
+ 2 files changed, 32 insertions(+), 1 deletion(-)
+
+diff --git python/samba/tests/krb5/s4u_tests.py python/samba/tests/krb5/s4u_tests.py
+index 5f37525f393..2953766ef21 100755
+--- python/samba/tests/krb5/s4u_tests.py
++++ python/samba/tests/krb5/s4u_tests.py
+@@ -36,6 +36,7 @@ from samba.tests.krb5.raw_testcase import (
+ from samba.tests.krb5.rfc4120_constants import (
+ AES256_CTS_HMAC_SHA1_96,
+ ARCFOUR_HMAC_MD5,
++ KDC_ERR_BADMATCH,
+ KDC_ERR_BADOPTION,
+ KDC_ERR_BAD_INTEGRITY,
+ KDC_ERR_GENERIC,
+@@ -243,7 +244,9 @@ class S4UKerberosTests(KDCBaseTest):
+ client_dn = client_creds.get_dn()
+ sid = self.get_objectSid(samdb, client_dn)
+
+- service_name = service_creds.get_username()[:-1]
++ service_name = kdc_dict.pop('service_name', None)
++ if service_name is None:
++ service_name = service_creds.get_username()[:-1]
+ service_sname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
+ names=['host', service_name])
+
+@@ -474,6 +477,33 @@ class S4UKerberosTests(KDCBaseTest):
+ 'expected_flags': 'forwardable'
+ })
+
++ # Do an S4U2Self with the sname in the request different to that of the
++ # service. We expect an error.
++ def test_s4u2self_wrong_sname(self):
++ other_creds = self.get_cached_creds(
++ account_type=self.AccountType.COMPUTER,
++ opts={
++ 'trusted_to_auth_for_delegation': True,
++ 'id': 0
++ })
++ other_sname = other_creds.get_username()[:-1]
++
++ self._run_s4u2self_test(
++ {
++ 'expected_error_mode': KDC_ERR_BADMATCH,
++ 'expect_edata': False,
++ 'client_opts': {
++ 'not_delegated': False
++ },
++ 'service_opts': {
++ 'trusted_to_auth_for_delegation': True
++ },
++ 'service_name': other_sname,
++ 'kdc_options': 'forwardable',
++ 'modify_service_tgt_fn': functools.partial(
++ self.set_ticket_forwardable, flag=True)
++ })
++
+ def _run_delegation_test(self, kdc_dict):
+ client_opts = kdc_dict.pop('client_opts', None)
+ client_creds = self.get_cached_creds(
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index bc644587319..483145f1473 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -243,6 +243,7 @@
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_service_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_wrong_sname
+ #
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_auth_data_required
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_auth_data_required
+--
+2.25.1
+
+
+From 46b05cbf803c54cf56dca228fe95a3454027d0cc Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 23 Nov 2021 20:00:07 +1300
+Subject: [PATCH 40/99] kdc: Match Windows error code for mismatching sname
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit b6a25f5f016aef39c3b1d7be8b3ecfe021c03c83)
+---
+ selftest/knownfail_heimdal_kdc | 3 ---
+ source4/kdc/db-glue.c | 2 +-
+ 2 files changed, 1 insertion(+), 4 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 483145f1473..981d7894158 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -243,7 +243,6 @@
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_service_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_wrong_sname
+ #
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_auth_data_required
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_auth_data_required
+@@ -292,6 +291,4 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_validate
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_false
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_sname_krbtgt
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed
+diff --git source4/kdc/db-glue.c source4/kdc/db-glue.c
+index d017741e30a..bed0ff773f9 100644
+--- source4/kdc/db-glue.c
++++ source4/kdc/db-glue.c
+@@ -2599,7 +2599,7 @@ samba_kdc_check_s4u2self(krb5_context context,
+ */
+ if (!(orig_sid && target_sid && dom_sid_equal(orig_sid, target_sid))) {
+ talloc_free(frame);
+- return KRB5KDC_ERR_BADOPTION;
++ return KRB5KRB_AP_ERR_BADMATCH;
+ }
+
+ talloc_free(frame);
+--
+2.25.1
+
+
+From 93a5264dd68da57e172af50020f670631eeef263 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 23 Nov 2021 20:15:41 +1300
+Subject: [PATCH 41/99] kdc: Always add the PAC if the header TGT is from an
+ RODC
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 690a00a40c0a3f77da6e4dca42b630f2793a98b8)
+---
+ selftest/knownfail_heimdal_kdc | 1 -
+ source4/kdc/wdc-samba4.c | 2 +-
+ 2 files changed, 1 insertion(+), 2 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 981d7894158..94a4509f45a 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -290,5 +290,4 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_validate
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_validate
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_pac_request_false
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed
+diff --git source4/kdc/wdc-samba4.c source4/kdc/wdc-samba4.c
+index ecd182702c3..8c3ce71529c 100644
+--- source4/kdc/wdc-samba4.c
++++ source4/kdc/wdc-samba4.c
+@@ -471,7 +471,7 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context,
+ goto out;
+ }
+
+- if (!server_skdc_entry->is_krbtgt) {
++ if (!is_untrusted && !server_skdc_entry->is_krbtgt) {
+ /*
+ * The client may have requested no PAC when obtaining the
+ * TGT.
+--
+2.25.1
+
+
+From 4cd44326ce38187965c46c71322caedb7a2fbf6c Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 25 Nov 2021 10:32:44 +1300
+Subject: [PATCH 42/99] tests/krb5: Add tests for renewal and validation of
+ RODC TGTs with PAC requests
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 73a48063469205099f02efdf3b8f0f1040dc7a3d)
+---
+ python/samba/tests/krb5/kdc_tgs_tests.py | 90 ++++++++++++++++++++++++
+ selftest/knownfail_heimdal_kdc | 6 ++
+ selftest/knownfail_mit_kdc | 6 ++
+ 3 files changed, 102 insertions(+)
+
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index ecc38538e61..2923d53772a 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -1867,6 +1867,51 @@ class KdcTgsTests(KDCBaseTest):
+ pac = self.get_ticket_pac(ticket)
+ self.assertIsNotNone(pac)
+
++ def test_rodc_renew_pac_request_none(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++ tgt = self.get_tgt(creds, pac_request=None)
++ tgt = self._modify_tgt(tgt, renewable=True, from_rodc=True)
++
++ tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
++
++ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
++
++ pac = self.get_ticket_pac(ticket)
++ self.assertIsNotNone(pac)
++
++ def test_rodc_renew_pac_request_false(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None)
++ tgt = self._modify_tgt(tgt, renewable=True, from_rodc=True)
++
++ tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
++
++ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
++
++ pac = self.get_ticket_pac(ticket)
++ self.assertIsNotNone(pac)
++
++ def test_rodc_renew_pac_request_true(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++ tgt = self.get_tgt(creds, pac_request=True)
++ tgt = self._modify_tgt(tgt, renewable=True, from_rodc=True)
++
++ tgt = self._renew_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
++
++ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
++
++ pac = self.get_ticket_pac(ticket)
++ self.assertIsNotNone(pac)
++
+ def test_validate_pac_request_none(self):
+ creds = self._get_creds()
+ tgt = self.get_tgt(creds, pac_request=None)
+@@ -1912,6 +1957,51 @@ class KdcTgsTests(KDCBaseTest):
+ pac = self.get_ticket_pac(ticket)
+ self.assertIsNotNone(pac)
+
++ def test_rodc_validate_pac_request_none(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++ tgt = self.get_tgt(creds, pac_request=None)
++ tgt = self._modify_tgt(tgt, invalid=True, from_rodc=True)
++
++ tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
++
++ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
++
++ pac = self.get_ticket_pac(ticket)
++ self.assertIsNotNone(pac)
++
++ def test_rodc_validate_pac_request_false(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++ tgt = self.get_tgt(creds, pac_request=False, expect_pac=None)
++ tgt = self._modify_tgt(tgt, invalid=True, from_rodc=True)
++
++ tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
++
++ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
++
++ pac = self.get_ticket_pac(ticket)
++ self.assertIsNotNone(pac)
++
++ def test_rodc_validate_pac_request_true(self):
++ creds = self._get_creds(replication_allowed=True,
++ revealed_to_rodc=True)
++ tgt = self.get_tgt(creds, pac_request=True)
++ tgt = self._modify_tgt(tgt, invalid=True, from_rodc=True)
++
++ tgt = self._validate_tgt(tgt, expected_error=0, expect_pac=None,
++ expect_pac_attrs=False,
++ expect_requester_sid=True)
++
++ ticket = self._run_tgs(tgt, expected_error=0, expect_pac=True)
++
++ pac = self.get_ticket_pac(ticket)
++ self.assertIsNotNone(pac)
++
+ def test_s4u2self_pac_request_none(self):
+ creds = self._get_creds()
+ tgt = self.get_tgt(creds, pac_request=None)
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 94a4509f45a..2de898e73c2 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -278,6 +278,12 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_revealed
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_renew_pac_request_false
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_renew_pac_request_none
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_renew_pac_request_true
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_false
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_none
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_false
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_true
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index c6dc1285837..73e64145e42 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -422,6 +422,12 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_service_ticket
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_renew_pac_request_false
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_renew_pac_request_none
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_renew_pac_request_true
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_false
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_none
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_req
+--
+2.25.1
+
+
+From 925f63f3e464c0fdb91aaa5ed523a6ddb481bfff Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 25 Nov 2021 13:24:57 +1300
+Subject: [PATCH 43/99] Revert "CVE-2020-25719 s4/torture: Expect additional
+ PAC buffers"
+
+This reverts commit fa4c9bcefdeed0a7106aab84df20b02435febc1f.
+
+We should not be generating these additional PAC buffers for service
+tickets, only for TGTs.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit e61983c7f2c4daade83b237efb990d0c0645b3a3)
+---
+ selftest/knownfail_heimdal_kdc | 39 ++++++++++++++++++++++++++++++++
+ source4/torture/rpc/remote_pac.c | 24 ++------------------
+ 2 files changed, 41 insertions(+), 22 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 2de898e73c2..65e4fee9510 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -297,3 +297,42 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_validate
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed
++#
++# PAC tests
++#
++^samba4.blackbox.pkinit_pac.STEP1 remote.pac verification.ad_dc:local
++^samba4.blackbox.pkinit_pac.STEP1 remote.pac verification.ad_dc_ntvfs:local
++^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc:local
++^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc_ntvfs:local
++^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc:local
++^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc_ntvfs:local
++^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc:local
++^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc_ntvfs:local
++^samba4.blackbox.pkinit_pac.netr-mem-arcfour.s4u2proxy-arcfour.ad_dc:local
++^samba4.blackbox.pkinit_pac.netr-mem-arcfour.s4u2proxy-arcfour.ad_dc_ntvfs:local
++^samba4.blackbox.pkinit_pac.netr-mem-arcfour.verify-sig-arcfour.ad_dc:local
++^samba4.blackbox.pkinit_pac.netr-mem-arcfour.verify-sig-arcfour.ad_dc_ntvfs:local
++^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2000dc
++^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2003dc
++^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2008dc
++^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2008r2dc
++^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2000dc
++^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2003dc
++^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2008dc
++^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2008r2dc
++^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2000dc
++^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2003dc
++^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2008dc
++^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2008r2dc
++^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2000dc
++^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2003dc
++^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2008dc
++^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2008r2dc
++^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2000dc
++^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2003dc
++^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2008dc
++^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2008r2dc
++^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2000dc
++^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2003dc
++^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2008dc
++^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2008r2dc
+diff --git source4/torture/rpc/remote_pac.c source4/torture/rpc/remote_pac.c
+index c94decef5ce..14c23f674f1 100644
+--- source4/torture/rpc/remote_pac.c
++++ source4/torture/rpc/remote_pac.c
+@@ -266,7 +266,7 @@ static bool test_PACVerify(struct torture_context *tctx,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+ torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_pull_struct_blob of PAC_DATA structure failed");
+
+- num_pac_buffers = 7;
++ num_pac_buffers = 5;
+ if (expect_pac_upn_dns_info) {
+ num_pac_buffers += 1;
+ }
+@@ -323,18 +323,6 @@ static bool test_PACVerify(struct torture_context *tctx,
+ pac_buf->info != NULL,
+ "PAC_TYPE_TICKET_CHECKSUM info");
+
+- pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_ATTRIBUTES_INFO);
+- torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_ATTRIBUTES_INFO");
+- torture_assert(tctx,
+- pac_buf->info != NULL,
+- "PAC_TYPE_ATTRIBUTES_INFO info");
+-
+- pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_REQUESTER_SID);
+- torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_REQUESTER_SID");
+- torture_assert(tctx,
+- pac_buf->info != NULL,
+- "PAC_TYPE_REQUESTER_SID info");
+-
+ ok = netlogon_validate_pac(tctx, p, server_creds, secure_channel_type, test_machine_name,
+ negotiate_flags, pac_data, session_info);
+
+@@ -1094,7 +1082,7 @@ static bool test_S4U2Proxy(struct torture_context *tctx,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+ torture_assert(tctx, NDR_ERR_CODE_IS_SUCCESS(ndr_err), "ndr_pull_struct_blob of PAC_DATA structure failed");
+
+- num_pac_buffers = 9;
++ num_pac_buffers = 7;
+
+ torture_assert_int_equal(tctx, pac_data_struct.version, 0, "version");
+ torture_assert_int_equal(tctx, pac_data_struct.num_buffers, num_pac_buffers, "num_buffers");
+@@ -1134,14 +1122,6 @@ static bool test_S4U2Proxy(struct torture_context *tctx,
+ talloc_asprintf(tctx, "%s@%s", self_princ, cli_credentials_get_realm(credentials)),
+ "wrong transited_services[0]");
+
+- pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_ATTRIBUTES_INFO);
+- torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_ATTRIBUTES_INFO");
+- torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_ATTRIBUTES_INFO info");
+-
+- pac_buf = get_pac_buffer(&pac_data_struct, PAC_TYPE_REQUESTER_SID);
+- torture_assert_not_null(tctx, pac_buf, "PAC_TYPE_REQUESTER_SID");
+- torture_assert_not_null(tctx, pac_buf->info, "PAC_TYPE_REQUESTER_SID info");
+-
+ return netlogon_validate_pac(tctx, p, server_creds, secure_channel_type, test_machine_name,
+ negotiate_flags, pac_data, session_info);
+ }
+--
+2.25.1
+
+
+From 72afa2641c24bd18a32463f0b0de7e91feb54290 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 20:42:22 +1300
+Subject: [PATCH 44/99] kdc: Don't include extra PAC buffers in service tickets
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 90025b6a4d250a15c0f988a9a9150ecfb63069ef)
+---
+ selftest/knownfail_heimdal_kdc | 42 ----------------------------------
+ source4/kdc/wdc-samba4.c | 31 +++++++++++++++++--------
+ 2 files changed, 21 insertions(+), 52 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 65e4fee9510..ea08cb44122 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -285,11 +285,8 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_false
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_none
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_no_requester_sid
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid(?!_)
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_validate
+@@ -297,42 +294,3 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_validate
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed
+-#
+-# PAC tests
+-#
+-^samba4.blackbox.pkinit_pac.STEP1 remote.pac verification.ad_dc:local
+-^samba4.blackbox.pkinit_pac.STEP1 remote.pac verification.ad_dc_ntvfs:local
+-^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc:local
+-^samba4.blackbox.pkinit_pac.netr-bdc-aes.verify-sig-aes.ad_dc_ntvfs:local
+-^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc:local
+-^samba4.blackbox.pkinit_pac.netr-mem-aes.s4u2proxy-aes.ad_dc_ntvfs:local
+-^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc:local
+-^samba4.blackbox.pkinit_pac.netr-mem-aes.verify-sig-aes.ad_dc_ntvfs:local
+-^samba4.blackbox.pkinit_pac.netr-mem-arcfour.s4u2proxy-arcfour.ad_dc:local
+-^samba4.blackbox.pkinit_pac.netr-mem-arcfour.s4u2proxy-arcfour.ad_dc_ntvfs:local
+-^samba4.blackbox.pkinit_pac.netr-mem-arcfour.verify-sig-arcfour.ad_dc:local
+-^samba4.blackbox.pkinit_pac.netr-mem-arcfour.verify-sig-arcfour.ad_dc_ntvfs:local
+-^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2000dc
+-^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2003dc
+-^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2008dc
+-^samba4.rpc.pac on ncacn_np.netr-bdc-aes.verify-sig-aes.fl2008r2dc
+-^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2000dc
+-^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2003dc
+-^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2008dc
+-^samba4.rpc.pac on ncacn_np.netr-bdc-arcfour.verify-sig-arcfour.fl2008r2dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2000dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2003dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2008dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-aes.s4u2proxy-aes.fl2008r2dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2000dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2003dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2008dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-aes.verify-sig-aes.fl2008r2dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2000dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2003dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2008dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.s4u2proxy-arcfour.fl2008r2dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2000dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2003dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2008dc
+-^samba4.rpc.pac on ncacn_np.netr-mem-arcfour.verify-sig-arcfour.fl2008r2dc
+diff --git source4/kdc/wdc-samba4.c source4/kdc/wdc-samba4.c
+index 8c3ce71529c..17af76f4edb 100644
+--- source4/kdc/wdc-samba4.c
++++ source4/kdc/wdc-samba4.c
+@@ -132,6 +132,7 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context,
+ krb5_error_code ret;
+ NTSTATUS nt_status;
+ bool is_in_db, is_untrusted;
++ bool is_krbtgt;
+ size_t num_types = 0;
+ uint32_t *types = NULL;
+ uint32_t forced_next_type = 0;
+@@ -471,7 +472,9 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context,
+ goto out;
+ }
+
+- if (!is_untrusted && !server_skdc_entry->is_krbtgt) {
++ is_krbtgt = krb5_principal_is_krbtgt(context, server->entry.principal);
++
++ if (!is_untrusted && !is_krbtgt) {
+ /*
+ * The client may have requested no PAC when obtaining the
+ * TGT.
+@@ -576,17 +579,25 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context,
+ type_blob = data_blob_const(&zero_byte, 1);
+ break;
+ case PAC_TYPE_ATTRIBUTES_INFO:
+- /* just copy... */
+- break;
++ if (is_krbtgt) {
++ /* just copy... */
++ break;
++ } else {
++ continue;
++ }
+ case PAC_TYPE_REQUESTER_SID:
+- /*
+- * Replace in the RODC case, otherwise
+- * requester_sid_blob is NULL and we just copy.
+- */
+- if (requester_sid_blob != NULL) {
+- type_blob = *requester_sid_blob;
++ if (is_krbtgt) {
++ /*
++ * Replace in the RODC case, otherwise
++ * requester_sid_blob is NULL and we just copy.
++ */
++ if (requester_sid_blob != NULL) {
++ type_blob = *requester_sid_blob;
++ }
++ break;
++ } else {
++ continue;
+ }
+- break;
+ default:
+ /* just copy... */
+ break;
+--
+2.25.1
+
+
+From 29f15fe2d92831dcf5f4eb6d295df866ff689ee3 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 25 Nov 2021 10:53:49 +1300
+Subject: [PATCH 45/99] kdc: Remove PAC_TYPE_ATTRIBUTES_INFO from RODC-issued
+ tickets
+
+Windows ignores PAC_TYPE_ATTRIBUTES_INFO and always issues a PAC when
+presented with an RODC-issued TGT. By removing this PAC buffer from
+RODC-issued tickets, we ensure that an RODC-issued ticket will still
+result in a PAC if it is first renewed or validated by the main DC.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 4b60e9516497c2e7f1545fe50887d0336b9893f2)
+---
+ selftest/knownfail_heimdal_kdc | 13 -------------
+ source4/kdc/wdc-samba4.c | 2 +-
+ 2 files changed, 1 insertion(+), 14 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index ea08cb44122..5e94cb63d7a 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -274,16 +274,6 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_false
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_none
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_true
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_revealed
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_renew_pac_request_false
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_renew_pac_request_none
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_renew_pac_request_true
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_false
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_none
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_rodc_validate_pac_request_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_false
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_no_requester_sid
+@@ -291,6 +281,3 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_validate
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_validate
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_validate
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_revealed
+diff --git source4/kdc/wdc-samba4.c source4/kdc/wdc-samba4.c
+index 17af76f4edb..713720bcb99 100644
+--- source4/kdc/wdc-samba4.c
++++ source4/kdc/wdc-samba4.c
+@@ -579,7 +579,7 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context,
+ type_blob = data_blob_const(&zero_byte, 1);
+ break;
+ case PAC_TYPE_ATTRIBUTES_INFO:
+- if (is_krbtgt) {
++ if (!is_untrusted && is_krbtgt) {
+ /* just copy... */
+ break;
+ } else {
+--
+2.25.1
+
+
+From d3436300745c41226d7ed146f269c929133f8f49 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 25 Nov 2021 12:46:40 +1300
+Subject: [PATCH 46/99] tests/krb5: Add a test for S4U2Self with no
+ authorization data required
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 192d6edfe912105ec344dc554f872a24c03540a3)
+---
+ python/samba/tests/krb5/s4u_tests.py | 34 ++++++++++++++++++++++++++++
+ selftest/knownfail_heimdal_kdc | 1 +
+ 2 files changed, 35 insertions(+)
+
+diff --git python/samba/tests/krb5/s4u_tests.py python/samba/tests/krb5/s4u_tests.py
+index 2953766ef21..6ec9af11423 100755
+--- python/samba/tests/krb5/s4u_tests.py
++++ python/samba/tests/krb5/s4u_tests.py
+@@ -324,6 +324,13 @@ class S4UKerberosTests(KDCBaseTest):
+ sname=service_sname,
+ etypes=etypes)
+
++ if not expected_error_mode:
++ # Check that the ticket contains a PAC.
++ ticket = kdc_exchange_dict['rep_ticket_creds']
++
++ pac = self.get_ticket_pac(ticket)
++ self.assertIsNotNone(pac)
++
+ # Ensure we used all the parameters given to us.
+ self.assertEqual({}, kdc_dict)
+
+@@ -504,6 +511,24 @@ class S4UKerberosTests(KDCBaseTest):
+ self.set_ticket_forwardable, flag=True)
+ })
+
++ # Do an S4U2Self where the service does not require authorization data. The
++ # resulting ticket should still contain a PAC.
++ def test_s4u2self_no_auth_data_required(self):
++ self._run_s4u2self_test(
++ {
++ 'client_opts': {
++ 'not_delegated': False
++ },
++ 'service_opts': {
++ 'trusted_to_auth_for_delegation': True,
++ 'no_auth_data_required': True
++ },
++ 'kdc_options': 'forwardable',
++ 'modify_service_tgt_fn': functools.partial(
++ self.set_ticket_forwardable, flag=True),
++ 'expected_flags': 'forwardable'
++ })
++
+ def _run_delegation_test(self, kdc_dict):
+ client_opts = kdc_dict.pop('client_opts', None)
+ client_creds = self.get_cached_creds(
+@@ -654,6 +679,15 @@ class S4UKerberosTests(KDCBaseTest):
+ etypes=etypes,
+ additional_tickets=additional_tickets)
+
++ if not expected_error_mode:
++ # Check whether the ticket contains a PAC.
++ ticket = kdc_exchange_dict['rep_ticket_creds']
++ pac = self.get_ticket_pac(ticket, expect_pac=expect_pac)
++ if expect_pac:
++ self.assertIsNotNone(pac)
++ else:
++ self.assertIsNone(pac)
++
+ # Ensure we used all the parameters given to us.
+ self.assertEqual({}, kdc_dict)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 5e94cb63d7a..2025032a278 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -242,6 +242,7 @@
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_client_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_service_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_no_auth_data_required
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed
+ #
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_auth_data_required
+--
+2.25.1
+
+
+From 8f97f78dd8023d88d76fc7de063661d94ebe5400 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 23 Nov 2021 17:30:50 +1300
+Subject: [PATCH 47/99] heimdal:kdc: Always generate a PAC for S4U2Self
+
+If we decided not to put a PAC into the ticket, mspac would be NULL
+here, and the resulting ticket would not contain a PAC. This could
+happen if there was a request to omit the PAC or the service did not
+require authorization data. Ensure that we always generate a PAC.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 1f4f3018c5001b289b91959a72d00575c8fc0ac1)
+---
+ selftest/knownfail_heimdal_kdc | 2 --
+ source4/heimdal/kdc/krb5tgs.c | 13 +++++++------
+ 2 files changed, 7 insertions(+), 8 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 2025032a278..53cc8e6b6a2 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -242,7 +242,6 @@
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_client_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_service_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_no_auth_data_required
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed
+ #
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_auth_data_required
+@@ -275,7 +274,6 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_false
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew
+diff --git source4/heimdal/kdc/krb5tgs.c source4/heimdal/kdc/krb5tgs.c
+index 6c5c51aa448..dc356b4daa5 100644
+--- source4/heimdal/kdc/krb5tgs.c
++++ source4/heimdal/kdc/krb5tgs.c
+@@ -1846,12 +1846,13 @@ server_lookup:
+ if (mspac) {
+ krb5_pac_free(context, mspac);
+ mspac = NULL;
+- ret = _kdc_pac_generate(context, s4u2self_impersonated_client, NULL, NULL, &mspac);
+- if (ret) {
+- kdc_log(context, config, 0, "PAC generation failed for -- %s",
+- tpn);
+- goto out;
+- }
++ }
++
++ ret = _kdc_pac_generate(context, s4u2self_impersonated_client, NULL, NULL, &mspac);
++ if (ret) {
++ kdc_log(context, config, 0, "PAC generation failed for -- %s",
++ tpn);
++ goto out;
+ }
+
+ /*
+--
+2.25.1
+
+
+From 8585333a8ef54295a60faf47689a8978c0740361 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 25 Nov 2021 09:29:42 +1300
+Subject: [PATCH 48/99] selftest: Properly check extra PAC buffers with Heimdal
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit ee4aa21c487fa80082a548b2e4f115a791e30340)
+
+[jsutton@samba.org Fixed conflicts]
+---
+ selftest/knownfail_heimdal_kdc | 12 ++++++++++++
+ source4/selftest/tests.py | 2 +-
+ 2 files changed, 13 insertions(+), 1 deletion(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 53cc8e6b6a2..f71b95f306e 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -241,8 +241,15 @@
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_unkeyed_service_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_client_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_service_checksum
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_client_not_delegated
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_no_auth_data_required
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_forwardable
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_nonempty_allowed
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_trusted_empty_allowed
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_trusted_nonempty_allowed
++^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_without_forwardable
+ #
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_auth_data_required
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_auth_data_required
+@@ -274,6 +281,11 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_false
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_none
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_true
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_req(?!_invalid)
++^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew
+
+
+From 65bb0e3201d60d87a3f228ea161644d9a5f918c5 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 23 Nov 2021 19:38:35 +1300
+Subject: [PATCH 49/99] heimdal:kdc: Do not generate extra PAC buffers for
+ S4U2Self service ticket
+
+Normally samba_wdc_get_pac() is used to generate the PAC for a TGT, but
+when generating a service ticket for S4U2Self, we want to avoid adding
+the additional PAC_ATTRIBUTES_INFO and PAC_REQUESTER_SID buffers.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 9bd26804852d957f81cb311e5142f9190f9afa65)
+---
+ selftest/knownfail_heimdal_kdc | 12 ------------
+ source4/heimdal/kdc/kerberos5.c | 2 +-
+ source4/heimdal/kdc/krb5tgs.c | 3 ++-
+ source4/heimdal/kdc/windc.c | 5 +++--
+ source4/heimdal/kdc/windc_plugin.h | 2 ++
+ source4/kdc/wdc-samba4.c | 11 ++++++++---
+ 6 files changed, 16 insertions(+), 19 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index f71b95f306e..53cc8e6b6a2 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -241,15 +241,8 @@
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_unkeyed_service_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_client_checksum
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_zeroed_service_checksum
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_client_not_delegated
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_forwardable
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_no_auth_data_required
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_forwardable
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_empty_allowed
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_not_trusted_nonempty_allowed
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_trusted_empty_allowed
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_trusted_nonempty_allowed
+-^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_s4u2self_without_forwardable
+ #
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_constrained_delegation_no_auth_data_required
+ ^samba.tests.krb5.s4u_tests.samba.tests.krb5.s4u_tests.S4UKerberosTests.test_rbcd_no_auth_data_required
+@@ -281,11 +274,6 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_false
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_none
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_pac_request_true
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_req(?!_invalid)
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_s4u2self_rodc_revealed
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew
+diff --git source4/heimdal/kdc/kerberos5.c source4/heimdal/kdc/kerberos5.c
+index 9684364c519..a9e81336615 100644
+--- source4/heimdal/kdc/kerberos5.c
++++ source4/heimdal/kdc/kerberos5.c
+@@ -1776,7 +1776,7 @@ _kdc_as_rep(krb5_context context,
+
+ sent_pac_request = send_pac_p(context, req, &pac_request);
+
+- ret = _kdc_pac_generate(context, client, pk_reply_key,
++ ret = _kdc_pac_generate(context, client, server, pk_reply_key,
+ sent_pac_request ? &pac_request : NULL,
+ &p);
+ if (ret) {
+diff --git source4/heimdal/kdc/krb5tgs.c source4/heimdal/kdc/krb5tgs.c
+index dc356b4daa5..38dba8493ae 100644
+--- source4/heimdal/kdc/krb5tgs.c
++++ source4/heimdal/kdc/krb5tgs.c
+@@ -1848,7 +1848,8 @@ server_lookup:
+ mspac = NULL;
+ }
+
+- ret = _kdc_pac_generate(context, s4u2self_impersonated_client, NULL, NULL, &mspac);
++ ret = _kdc_pac_generate(context, s4u2self_impersonated_client, server,
++ NULL, NULL, &mspac);
+ if (ret) {
+ kdc_log(context, config, 0, "PAC generation failed for -- %s",
+ tpn);
+diff --git source4/heimdal/kdc/windc.c source4/heimdal/kdc/windc.c
+index 93b973f576b..0a5ae5025ec 100644
+--- source4/heimdal/kdc/windc.c
++++ source4/heimdal/kdc/windc.c
+@@ -73,6 +73,7 @@ krb5_kdc_windc_init(krb5_context context)
+ krb5_error_code
+ _kdc_pac_generate(krb5_context context,
+ hdb_entry_ex *client,
++ hdb_entry_ex *server,
+ const krb5_keyblock *pk_reply_key,
+ const krb5_boolean *pac_request,
+ krb5_pac *pac)
+@@ -88,9 +89,9 @@ _kdc_pac_generate(krb5_context context,
+
+ if (windcft->pac_pk_generate != NULL && pk_reply_key != NULL)
+ return (windcft->pac_pk_generate)(windcctx, context,
+- client, pk_reply_key,
++ client, server, pk_reply_key,
+ pac_request, pac);
+- return (windcft->pac_generate)(windcctx, context, client,
++ return (windcft->pac_generate)(windcctx, context, client, server,
+ pac_request, pac);
+ }
+
+diff --git source4/heimdal/kdc/windc_plugin.h source4/heimdal/kdc/windc_plugin.h
+index c7f2bcb5ed9..d239d0260e7 100644
+--- source4/heimdal/kdc/windc_plugin.h
++++ source4/heimdal/kdc/windc_plugin.h
+@@ -55,12 +55,14 @@ struct hdb_entry_ex;
+ typedef krb5_error_code
+ (*krb5plugin_windc_pac_generate)(void *, krb5_context,
+ struct hdb_entry_ex *, /* client */
++ struct hdb_entry_ex *, /* server */
+ const krb5_boolean *, /* pac_request */
+ krb5_pac *);
+
+ typedef krb5_error_code
+ (*krb5plugin_windc_pac_pk_generate)(void *, krb5_context,
+ struct hdb_entry_ex *, /* client */
++ struct hdb_entry_ex *, /* server */
+ const krb5_keyblock *, /* pk_replykey */
+ const krb5_boolean *, /* pac_request */
+ krb5_pac *);
+diff --git source4/kdc/wdc-samba4.c source4/kdc/wdc-samba4.c
+index 713720bcb99..b1d011c09a9 100644
+--- source4/kdc/wdc-samba4.c
++++ source4/kdc/wdc-samba4.c
+@@ -37,6 +37,7 @@
+ */
+ static krb5_error_code samba_wdc_get_pac(void *priv, krb5_context context,
+ struct hdb_entry_ex *client,
++ struct hdb_entry_ex *server,
+ const krb5_keyblock *pk_reply_key,
+ const krb5_boolean *pac_request,
+ krb5_pac *pac)
+@@ -55,6 +56,7 @@ static krb5_error_code samba_wdc_get_pac(void *priv, krb5_context context,
+ struct samba_kdc_entry *skdc_entry =
+ talloc_get_type_abort(client->ctx,
+ struct samba_kdc_entry);
++ bool is_krbtgt;
+
+ mem_ctx = talloc_named(client->ctx, 0, "samba_get_pac context");
+ if (!mem_ctx) {
+@@ -65,13 +67,15 @@ static krb5_error_code samba_wdc_get_pac(void *priv, krb5_context context,
+ cred_ndr_ptr = &cred_ndr;
+ }
+
++ is_krbtgt = krb5_principal_is_krbtgt(context, server->entry.principal);
++
+ nt_status = samba_kdc_get_pac_blobs(mem_ctx, skdc_entry,
+ &logon_blob,
+ cred_ndr_ptr,
+ &upn_blob,
+- &pac_attrs_blob,
++ is_krbtgt ? &pac_attrs_blob : NULL,
+ pac_request,
+- &requester_sid_blob,
++ is_krbtgt ? &requester_sid_blob : NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(mem_ctx);
+@@ -101,10 +105,11 @@ static krb5_error_code samba_wdc_get_pac(void *priv, krb5_context context,
+
+ static krb5_error_code samba_wdc_get_pac_compat(void *priv, krb5_context context,
+ struct hdb_entry_ex *client,
++ struct hdb_entry_ex *server,
+ const krb5_boolean *pac_request,
+ krb5_pac *pac)
+ {
+- return samba_wdc_get_pac(priv, context, client, NULL, pac_request, pac);
++ return samba_wdc_get_pac(priv, context, client, server, NULL, pac_request, pac);
+ }
+
+ static krb5_error_code samba_wdc_reget_pac2(krb5_context context,
+--
+2.25.1
+
+
+From 49aafce0a705d47ffd4753ce6c6f452c4f7aa882 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 24 Nov 2021 20:41:54 +1300
+Subject: [PATCH 50/99] kdc: Require that PAC_REQUESTER_SID buffer is present
+ for TGTs
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+
+Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
+Autobuild-Date(master): Tue Nov 30 03:33:26 UTC 2021 on sn-devel-184
+
+(cherry picked from commit 38c5bad4a853b19fe9a51fb059e150b153c4632a)
+---
+ selftest/knownfail_heimdal_kdc | 6 ------
+ source4/kdc/wdc-samba4.c | 6 ++++++
+ 2 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 53cc8e6b6a2..32465cb6042 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -274,9 +274,3 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_no_requester_sid
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_renew
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_validate
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_validate
+diff --git source4/kdc/wdc-samba4.c source4/kdc/wdc-samba4.c
+index b1d011c09a9..d7ce34fb3a9 100644
+--- source4/kdc/wdc-samba4.c
++++ source4/kdc/wdc-samba4.c
+@@ -459,6 +459,12 @@ static krb5_error_code samba_wdc_reget_pac2(krb5_context context,
+ talloc_free(mem_ctx);
+ return EINVAL;
+ }
++ if (delegated_proxy_principal == NULL && requester_sid_idx == -1) {
++ DEBUG(1, ("PAC_TYPE_REQUESTER_SID missing\n"));
++ SAFE_FREE(types);
++ talloc_free(mem_ctx);
++ return KRB5KDC_ERR_TGT_REVOKED;
++ }
+
+ /*
+ * The server account may be set not to want the PAC.
+--
+2.25.1
+
+
+From 3fc519edec0159535baa0b659861b73f40632110 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 7 Dec 2021 13:15:38 +1300
+Subject: [PATCH 51/99] kdc: Canonicalize realm for enterprise principals
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+
+Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
+Autobuild-Date(master): Tue Dec 7 04:54:35 UTC 2021 on sn-devel-184
+
+(cherry picked from commit 8bd7b316bd61ef35f6e0baa0b65f0ef00910112c)
+---
+ selftest/knownfail.d/kdc-enterprise | 63 -----------------------------
+ selftest/knownfail_heimdal_kdc | 3 --
+ selftest/knownfail_mit_kdc | 36 +++++++++++++++++
+ source4/kdc/db-glue.c | 24 +++++------
+ 4 files changed, 47 insertions(+), 79 deletions(-)
+ delete mode 100644 selftest/knownfail.d/kdc-enterprise
+
+diff --git selftest/knownfail.d/kdc-enterprise selftest/knownfail.d/kdc-enterprise
+deleted file mode 100644
+index c9b6c98a2ee..00000000000
+--- selftest/knownfail.d/kdc-enterprise
++++ /dev/null
+@@ -1,63 +0,0 @@
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_UPN\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_UPN_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UPN\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UPN_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_UPN\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_UPN_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_UPN\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_UPN_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_UPN\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_UPN_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_UPN\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_UPN_RemoveDollar\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise_NetbiosRealm\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise_NetbiosRealm_UPN\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise_UPN\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise_UpperRealm_NetbiosRealm\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise_UpperRealm_NetbiosRealm_UPN\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_UPN\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise_UpperUserName\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise_UpperUserName_NetbiosRealm\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise_UpperUserName_NetbiosRealm_UPN\(
+-samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_UserCredentials_Enterprise_UpperUserName_UPN\(
+-
+-
+-
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_RemoveDollar_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_UPN_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_UPN_RemoveDollar_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_RemoveDollar_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UPN_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UPN_RemoveDollar_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_RemoveDollar_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_UPN_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_UPN_RemoveDollar_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_RemoveDollar_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_UPN_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_UPN_RemoveDollar_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_RemoveDollar_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_UPN_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_UPN_RemoveDollar_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_RemoveDollar_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_UPN_AsReqSelf\(
+-^samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_UPN_RemoveDollar_AsReqSelf\(
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 32465cb6042..424a8b81c38 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -5,9 +5,6 @@
+ #
+ # Heimdal currently fails the following MS-KILE client principal lookup
+ # tests
+-^samba.tests.krb5.ms_kile_client_principal_lookup_tests.samba.tests.krb5.ms_kile_client_principal_lookup_tests.MS_Kile_Client_Principal_Lookup_Tests.test_enterprise_principal_step_1_3
+-^samba.tests.krb5.ms_kile_client_principal_lookup_tests.samba.tests.krb5.ms_kile_client_principal_lookup_tests.MS_Kile_Client_Principal_Lookup_Tests.test_enterprise_principal_step_4
+-^samba.tests.krb5.ms_kile_client_principal_lookup_tests.samba.tests.krb5.ms_kile_client_principal_lookup_tests.MS_Kile_Client_Principal_Lookup_Tests.test_enterprise_principal_step_5
+ ^samba.tests.krb5.ms_kile_client_principal_lookup_tests.samba.tests.krb5.ms_kile_client_principal_lookup_tests.MS_Kile_Client_Principal_Lookup_Tests.test_enterprise_principal_step_6_a
+ ^samba.tests.krb5.ms_kile_client_principal_lookup_tests.samba.tests.krb5.ms_kile_client_principal_lookup_tests.MS_Kile_Client_Principal_Lookup_Tests.test_nt_enterprise_principal_step_6_b
+ ^samba.tests.krb5.ms_kile_client_principal_lookup_tests.samba.tests.krb5.ms_kile_client_principal_lookup_tests.MS_Kile_Client_Principal_Lookup_Tests.test_nt_principal_step_4_a
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 73e64145e42..4d685af7140 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -56,17 +56,53 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Canonicalize_UpperUserName_UPN\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Canonicalize_UpperUserName_UPN_RemoveDollar\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_RemoveDollar_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_UPN\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_UPN_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_UPN_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_NetbiosRealm_UPN_RemoveDollar_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_RemoveDollar_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UPN\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UPN_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UPN_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UPN_RemoveDollar_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_RemoveDollar_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_UPN\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_UPN_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_UPN_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_NetbiosRealm_UPN_RemoveDollar_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_RemoveDollar_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_UPN\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_UPN_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_UPN_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperRealm_UpperUserName_NetbiosRealm_UPN_RemoveDollar_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_RemoveDollar_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_UPN\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_UPN_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_UPN_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_NetbiosRealm_UPN_RemoveDollar_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_RemoveDollar_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_UPN\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_UPN_AsReqSelf\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_UPN_RemoveDollar\(
++samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_Enterprise_UpperUserName_UPN_RemoveDollar_AsReqSelf\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_NetbiosRealm\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_NetbiosRealm_RemoveDollar\(
+ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_tests.KerberosASCanonicalizationTests.test_MachineCredentials_NetbiosRealm_UPN\(
+diff --git source4/kdc/db-glue.c source4/kdc/db-glue.c
+index bed0ff773f9..5752ffb821c 100644
+--- source4/kdc/db-glue.c
++++ source4/kdc/db-glue.c
+@@ -980,19 +980,17 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
+ goto out;
+ }
+
+- if (smb_krb5_principal_get_type(context, principal) != KRB5_NT_ENTERPRISE_PRINCIPAL) {
+- /* While we have copied the client principal, tests
+- * show that Win2k3 returns the 'corrected' realm, not
+- * the client-specified realm. This code attempts to
+- * replace the client principal's realm with the one
+- * we determine from our records */
+-
+- /* this has to be with malloc() */
+- ret = smb_krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx));
+- if (ret) {
+- krb5_clear_error_message(context);
+- goto out;
+- }
++ /* While we have copied the client principal, tests
++ * show that Win2k3 returns the 'corrected' realm, not
++ * the client-specified realm. This code attempts to
++ * replace the client principal's realm with the one
++ * we determine from our records */
++
++ /* this has to be with malloc() */
++ ret = smb_krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx));
++ if (ret) {
++ krb5_clear_error_message(context);
++ goto out;
+ }
+ }
+
+--
+2.25.1
+
+
+From 787405ef59b70cef011f005a6ed98898c5d43adb Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 14 Dec 2021 19:16:00 +1300
+Subject: [PATCH 52/99] tests/krb5: Correctly determine whether tickets are
+ service tickets
+
+Previously we expected tickets to contain a ticket checksum if the sname
+was not the krbtgt. However, the ticket checksum should not be present
+if we are performing an AS-REQ to our own account. Now we determine a
+ticket is a service ticket only if the request is also a TGS-REQ.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+(cherry picked from commit 100be7eb8e70ba270a8e92957a5e47466160a901)
+---
+ python/samba/tests/krb5/compatability_tests.py | 10 ++++++----
+ python/samba/tests/krb5/kdc_base_test.py | 2 +-
+ python/samba/tests/krb5/raw_testcase.py | 18 ++++++++++--------
+ python/samba/tests/krb5/rodc_tests.py | 4 ++--
+ 4 files changed, 19 insertions(+), 15 deletions(-)
+
+diff --git python/samba/tests/krb5/compatability_tests.py python/samba/tests/krb5/compatability_tests.py
+index ed2dc565b6d..65e9e3788d5 100755
+--- python/samba/tests/krb5/compatability_tests.py
++++ python/samba/tests/krb5/compatability_tests.py
+@@ -132,13 +132,14 @@ class SimpleKerberosTests(KDCBaseTest):
+ tgt = self.get_tgt(user_creds)
+
+ # Ensure the PAC contains the expected checksums.
+- self.verify_ticket(tgt, key)
++ self.verify_ticket(tgt, key, service_ticket=False)
+
+ # Get a service ticket from the DC.
+ service_ticket = self.get_service_ticket(tgt, target_creds)
+
+ # Ensure the PAC contains the expected checksums.
+- self.verify_ticket(service_ticket, key, expect_ticket_checksum=True)
++ self.verify_ticket(service_ticket, key, service_ticket=True,
++ expect_ticket_checksum=True)
+
+ def test_mit_ticket_signature(self):
+ # Ensure that a DC does not issue tickets signed with its krbtgt key.
+@@ -152,13 +153,14 @@ class SimpleKerberosTests(KDCBaseTest):
+ tgt = self.get_tgt(user_creds)
+
+ # Ensure the PAC contains the expected checksums.
+- self.verify_ticket(tgt, key)
++ self.verify_ticket(tgt, key, service_ticket=False)
+
+ # Get a service ticket from the DC.
+ service_ticket = self.get_service_ticket(tgt, target_creds)
+
+ # Ensure the PAC does not contain the expected checksums.
+- self.verify_ticket(service_ticket, key, expect_ticket_checksum=False)
++ self.verify_ticket(service_ticket, key, service_ticket=True,
++ expect_ticket_checksum=False)
+
+ def as_pre_auth_req(self, creds, etypes):
+ user = creds.get_username()
+diff --git python/samba/tests/krb5/kdc_base_test.py python/samba/tests/krb5/kdc_base_test.py
+index 6e96b982167..9506048ee2a 100644
+--- python/samba/tests/krb5/kdc_base_test.py
++++ python/samba/tests/krb5/kdc_base_test.py
+@@ -1395,7 +1395,7 @@ class KDCBaseTest(RawKerberosTest):
+ krbtgt_creds = self.get_krbtgt_creds()
+ krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds)
+ self.verify_ticket(service_ticket_creds, krbtgt_key,
+- expect_pac=expect_pac,
++ service_ticket=True, expect_pac=expect_pac,
+ expect_ticket_checksum=self.tkt_sig_support)
+
+ self.tkt_cache[cache_key] = service_ticket_creds
+diff --git python/samba/tests/krb5/raw_testcase.py python/samba/tests/krb5/raw_testcase.py
+index 14e655313fc..a2241707d44 100644
+--- python/samba/tests/krb5/raw_testcase.py
++++ python/samba/tests/krb5/raw_testcase.py
+@@ -2587,7 +2587,11 @@ class RawKerberosTest(TestCaseInTempDir):
+ self.assertIsNotNone(ticket_decryption_key)
+
+ if ticket_decryption_key is not None:
+- self.verify_ticket(ticket_creds, krbtgt_keys, expect_pac=expect_pac,
++ service_ticket = (not self.is_tgs(expected_sname)
++ and rep_msg_type == KRB_TGS_REP)
++ self.verify_ticket(ticket_creds, krbtgt_keys,
++ service_ticket=service_ticket,
++ expect_pac=expect_pac,
+ expect_ticket_checksum=expect_ticket_checksum
+ or self.tkt_sig_support)
+
+@@ -2624,14 +2628,14 @@ class RawKerberosTest(TestCaseInTempDir):
+ expected_types.append(krb5pac.PAC_TYPE_DEVICE_INFO)
+ expected_types.append(krb5pac.PAC_TYPE_DEVICE_CLAIMS_INFO)
+
+- if not self.is_tgs(expected_sname):
++ if not self.is_tgs(expected_sname) and rep_msg_type == KRB_TGS_REP:
+ expected_types.append(krb5pac.PAC_TYPE_TICKET_CHECKSUM)
+
+ require_strict = {krb5pac.PAC_TYPE_CLIENT_CLAIMS_INFO}
+ if not self.tkt_sig_support:
+ require_strict.add(krb5pac.PAC_TYPE_TICKET_CHECKSUM)
+
+- expect_extra_pac_buffers = rep_msg_type == KRB_AS_REP
++ expect_extra_pac_buffers = self.is_tgs(expected_sname)
+
+ expect_pac_attrs = kdc_exchange_dict['expect_pac_attrs']
+
+@@ -3233,11 +3237,9 @@ class RawKerberosTest(TestCaseInTempDir):
+ ticket_blob)
+ self.assertEqual(expected_checksum, checksum)
+
+- def verify_ticket(self, ticket, krbtgt_keys, expect_pac=True,
++ def verify_ticket(self, ticket, krbtgt_keys, service_ticket,
++ expect_pac=True,
+ expect_ticket_checksum=True):
+- # Check if the ticket is a TGT.
+- is_tgt = self.is_tgt(ticket)
+-
+ # Decrypt the ticket.
+
+ key = ticket.decryption_key
+@@ -3336,7 +3338,7 @@ class RawKerberosTest(TestCaseInTempDir):
+ kdc_ctype,
+ kdc_checksum)
+
+- if is_tgt:
++ if not service_ticket:
+ self.assertNotIn(krb5pac.PAC_TYPE_TICKET_CHECKSUM, checksums)
+ else:
+ ticket_checksum, ticket_ctype = checksums.get(
+diff --git python/samba/tests/krb5/rodc_tests.py python/samba/tests/krb5/rodc_tests.py
+index 0e252d90262..83ee35d650a 100755
+--- python/samba/tests/krb5/rodc_tests.py
++++ python/samba/tests/krb5/rodc_tests.py
+@@ -58,14 +58,14 @@ class RodcKerberosTests(KDCBaseTest):
+ tgt = self.get_tgt(user_creds, to_rodc=True)
+
+ # Ensure the PAC contains the expected checksums.
+- self.verify_ticket(tgt, rodc_key)
++ self.verify_ticket(tgt, rodc_key, service_ticket=False)
+
+ # Get a service ticket from the RODC.
+ service_ticket = self.get_service_ticket(tgt, target_creds,
+ to_rodc=True)
+
+ # Ensure the PAC contains the expected checksums.
+- self.verify_ticket(service_ticket, rodc_key)
++ self.verify_ticket(service_ticket, rodc_key, service_ticket=True)
+
+
+ if __name__ == "__main__":
+--
+2.25.1
+
+
+From c0977bee5b8c2f72cb5467e95a6ab034f696eee7 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 8 Feb 2022 12:15:36 +1300
+Subject: [PATCH 53/99] tests/krb5: Add helper function to modify ticket flags
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Stefan Metzmacher <metze@samba.org>
+(cherry picked from commit ded5115f73dff5b8b2f3212988e03f9dbe0c2aa3)
+---
+ python/samba/tests/krb5/kdc_base_test.py | 14 ++++++++++++++
+ python/samba/tests/krb5/kdc_tgs_tests.py | 18 ++----------------
+ python/samba/tests/krb5/s4u_tests.py | 17 +++--------------
+ 3 files changed, 19 insertions(+), 30 deletions(-)
+
+diff --git python/samba/tests/krb5/kdc_base_test.py python/samba/tests/krb5/kdc_base_test.py
+index 9506048ee2a..58b87eab25b 100644
+--- python/samba/tests/krb5/kdc_base_test.py
++++ python/samba/tests/krb5/kdc_base_test.py
+@@ -1602,6 +1602,20 @@ class KDCBaseTest(RawKerberosTest):
+ enc_part, asn1Spec=krb5_asn1.EncTicketPart())
+ return enc_ticket_part
+
++ def modify_ticket_flag(self, enc_part, flag, value):
++ self.assertIsInstance(value, bool)
++
++ flag = krb5_asn1.TicketFlags(flag)
++ pos = len(tuple(flag)) - 1
++
++ flags = enc_part['flags']
++ self.assertLessEqual(pos, len(flags))
++
++ new_flags = flags[:pos] + str(int(value)) + flags[pos + 1:]
++ enc_part['flags'] = new_flags
++
++ return enc_part
++
+ def get_objectSid(self, samdb, dn):
+ ''' Get the objectSID for a DN
+ Note: performs an Ldb query.
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index 2923d53772a..8cd27dec2aa 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -2177,14 +2177,7 @@ class KdcTgsTests(KDCBaseTest):
+
+ def _modify_renewable(self, enc_part):
+ # Set the renewable flag.
+- renewable_flag = krb5_asn1.TicketFlags('renewable')
+- pos = len(tuple(renewable_flag)) - 1
+-
+- flags = enc_part['flags']
+- self.assertLessEqual(pos, len(flags))
+-
+- new_flags = flags[:pos] + '1' + flags[pos + 1:]
+- enc_part['flags'] = new_flags
++ enc_part = self.modify_ticket_flag(enc_part, 'renewable', value=True)
+
+ # Set the renew-till time to be in the future.
+ renew_till = self.get_KerberosTime(offset=100 * 60 * 60)
+@@ -2194,14 +2187,7 @@ class KdcTgsTests(KDCBaseTest):
+
+ def _modify_invalid(self, enc_part):
+ # Set the invalid flag.
+- invalid_flag = krb5_asn1.TicketFlags('invalid')
+- pos = len(tuple(invalid_flag)) - 1
+-
+- flags = enc_part['flags']
+- self.assertLessEqual(pos, len(flags))
+-
+- new_flags = flags[:pos] + '1' + flags[pos + 1:]
+- enc_part['flags'] = new_flags
++ enc_part = self.modify_ticket_flag(enc_part, 'invalid', value=True)
+
+ # Set the ticket start time to be in the past.
+ past_time = self.get_KerberosTime(offset=-100 * 60 * 60)
+diff --git python/samba/tests/krb5/s4u_tests.py python/samba/tests/krb5/s4u_tests.py
+index 6ec9af11423..49dd89cd764 100755
+--- python/samba/tests/krb5/s4u_tests.py
++++ python/samba/tests/krb5/s4u_tests.py
+@@ -1336,20 +1336,9 @@ class S4UKerberosTests(KDCBaseTest):
+ modify_pac_fn=modify_pac_fn)
+
+ def set_ticket_forwardable(self, ticket, flag, update_pac_checksums=True):
+- flag = '1' if flag else '0'
+-
+- def modify_fn(enc_part):
+- # Reset the forwardable flag
+- forwardable_pos = (len(tuple(krb5_asn1.TicketFlags('forwardable')))
+- - 1)
+-
+- flags = enc_part['flags']
+- self.assertLessEqual(forwardable_pos, len(flags))
+- enc_part['flags'] = (flags[:forwardable_pos] +
+- flag +
+- flags[forwardable_pos+1:])
+-
+- return enc_part
++ modify_fn = functools.partial(self.modify_ticket_flag,
++ flag='forwardable',
++ value=flag)
+
+ if update_pac_checksums:
+ checksum_keys = self.get_krbtgt_checksum_key()
+--
+2.25.1
+
+
+From c0395578c50fbc4f1946e2f5a065d94f67212eb0 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 15 Jun 2022 19:37:39 +1200
+Subject: [PATCH 55/99] CVE-2022-2031 s4:kdc: Add MIT support for
+ ATTRIBUTES_INFO and REQUESTER_SID PAC buffers
+
+So that we do not confuse TGTs and kpasswd tickets, it is critical to
+check that the REQUESTER_SID buffer exists in TGTs, and to ensure that
+it is not propagated to service tickets.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+
+[jsutton@samba.org Brought in changes to add ATTRIBUTES_INFO and
+ REQUESTER_SID buffers to new PACs, and updated knownfails]
+
+[jsutton@samba.org Adjusted MIT knownfails]
+---
+ selftest/knownfail_mit_kdc | 17 -----
+ source4/kdc/mit-kdb/kdb_samba_policies.c | 5 +-
+ source4/kdc/mit_samba.c | 93 +++++++++++++++++++++++-
+ source4/kdc/mit_samba.h | 1 +
+ 4 files changed, 94 insertions(+), 22 deletions(-)
+
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 4d685af7140..108c6055d0c 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -445,7 +445,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_no_pac
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_pac_request_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_pac_request_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_req
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_renew_rodc_allowed_denied
+@@ -482,7 +481,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rename
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_invalid
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_no_requester_sid
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_allowed_denied
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_denied
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_no_krbtgt_link
+@@ -518,7 +516,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_user2user_wrong_srealm
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_authdata_no_pac
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_no_pac
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_pac_request_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_pac_request_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_req
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_validate_rodc_allowed_denied
+@@ -536,21 +533,17 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ #
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_false
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_false
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_renew_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_false
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_missing_rodc_renew_true
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_false
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_renew_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_false
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_rodc_renew_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_pac_attrs_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_false
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_none
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_pac_attrs_true
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_pac_attrs
+ #
+@@ -571,21 +564,11 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ #
+ # PAC requester SID tests
+ #
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_as_requester_sid
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_logon_info_sid_mismatch_nonexisting
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_existing
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_requester_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_req_from_rodc_no_requester_sid
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_rodc_validate
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_missing_validate
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_renew
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_rodc_validate
+-^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_requester_sid_validate
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_only_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_sid_mismatch_existing
+diff --git source4/kdc/mit-kdb/kdb_samba_policies.c source4/kdc/mit-kdb/kdb_samba_policies.c
+index 7bc9a7b3347..3b25fff410b 100644
+--- source4/kdc/mit-kdb/kdb_samba_policies.c
++++ source4/kdc/mit-kdb/kdb_samba_policies.c
+@@ -159,6 +159,7 @@ done:
+
+ static krb5_error_code ks_get_pac(krb5_context context,
+ krb5_db_entry *client,
++ krb5_db_entry *server,
+ krb5_keyblock *client_key,
+ krb5_pac *pac)
+ {
+@@ -173,6 +174,7 @@ static krb5_error_code ks_get_pac(krb5_context context,
+ code = mit_samba_get_pac(mit_ctx,
+ context,
+ client,
++ server,
+ client_key,
+ pac);
+ if (code != 0) {
+@@ -439,7 +441,7 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
+ */
+ if (with_pac && generate_pac) {
+ DBG_DEBUG("Generate PAC for AS-REQ [%s]\n", client_name);
+- code = ks_get_pac(context, client_entry, client_key, &pac);
++ code = ks_get_pac(context, client_entry, server, client_key, &pac);
+ if (code != 0) {
+ goto done;
+ }
+@@ -490,6 +492,7 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
+
+ code = ks_get_pac(context,
+ client_entry,
++ server,
+ client_key,
+ &pac);
+ if (code != 0 && code != ENOENT) {
+diff --git source4/kdc/mit_samba.c source4/kdc/mit_samba.c
+index c2a604045d9..df2ba0a906f 100644
+--- source4/kdc/mit_samba.c
++++ source4/kdc/mit_samba.c
+@@ -407,6 +407,7 @@ int mit_samba_get_nextkey(struct mit_samba_context *ctx,
+ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
+ krb5_context context,
+ krb5_db_entry *client,
++ krb5_db_entry *server,
+ krb5_keyblock *client_key,
+ krb5_pac *pac)
+ {
+@@ -417,9 +418,12 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
+ DATA_BLOB **cred_ndr_ptr = NULL;
+ DATA_BLOB cred_blob = data_blob_null;
+ DATA_BLOB *pcred_blob = NULL;
++ DATA_BLOB *pac_attrs_blob = NULL;
++ DATA_BLOB *requester_sid_blob = NULL;
+ NTSTATUS nt_status;
+ krb5_error_code code;
+ struct samba_kdc_entry *skdc_entry;
++ bool is_krbtgt;
+
+ skdc_entry = talloc_get_type_abort(client->e_data,
+ struct samba_kdc_entry);
+@@ -438,12 +442,16 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
+ }
+ #endif
+
++ is_krbtgt = ks_is_tgs_principal(smb_ctx, server->princ);
++
+ nt_status = samba_kdc_get_pac_blobs(tmp_ctx,
+ skdc_entry,
+ &logon_info_blob,
+ cred_ndr_ptr,
+ &upn_dns_info_blob,
+- NULL, NULL, NULL,
++ is_krbtgt ? &pac_attrs_blob : NULL,
++ NULL,
++ is_krbtgt ? &requester_sid_blob : NULL,
+ NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ talloc_free(tmp_ctx);
+@@ -471,8 +479,8 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
+ logon_info_blob,
+ pcred_blob,
+ upn_dns_info_blob,
+- NULL,
+- NULL,
++ pac_attrs_blob,
++ requester_sid_blob,
+ NULL,
+ pac);
+
+@@ -496,6 +504,7 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
+ DATA_BLOB *pac_blob = NULL;
+ DATA_BLOB *upn_blob = NULL;
+ DATA_BLOB *deleg_blob = NULL;
++ DATA_BLOB *requester_sid_blob = NULL;
+ struct samba_kdc_entry *client_skdc_entry = NULL;
+ struct samba_kdc_entry *krbtgt_skdc_entry = NULL;
+ struct samba_kdc_entry *server_skdc_entry = NULL;
+@@ -511,8 +520,12 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
+ ssize_t upn_dns_info_idx = -1;
+ ssize_t srv_checksum_idx = -1;
+ ssize_t kdc_checksum_idx = -1;
++ ssize_t tkt_checksum_idx = -1;
++ ssize_t attrs_info_idx = -1;
++ ssize_t requester_sid_idx = -1;
+ krb5_pac new_pac = NULL;
+ bool ok;
++ bool is_krbtgt;
+
+ /* Create a memory context early so code can use talloc_stackframe() */
+ tmp_ctx = talloc_named(ctx, 0, "mit_samba_reget_pac context");
+@@ -520,6 +533,8 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
+ return ENOMEM;
+ }
+
++ is_krbtgt = ks_is_tgs_principal(ctx, server->princ);
++
+ if (client != NULL) {
+ client_skdc_entry =
+ talloc_get_type_abort(client->e_data,
+@@ -578,7 +593,7 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
+ NULL,
+ &upn_blob,
+ NULL, NULL,
+- NULL,
++ &requester_sid_blob,
+ NULL);
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ code = EINVAL;
+@@ -737,6 +752,45 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
+ }
+ kdc_checksum_idx = i;
+ break;
++ case PAC_TYPE_TICKET_CHECKSUM:
++ if (tkt_checksum_idx != -1) {
++ DBG_WARNING("ticket checksum type[%u] twice "
++ "[%zd] and [%zu]: \n",
++ types[i],
++ tkt_checksum_idx,
++ i);
++ SAFE_FREE(types);
++ code = EINVAL;
++ goto done;
++ }
++ tkt_checksum_idx = i;
++ break;
++ case PAC_TYPE_ATTRIBUTES_INFO:
++ if (attrs_info_idx != -1) {
++ DBG_WARNING("attributes info type[%u] twice "
++ "[%zd] and [%zu]: \n",
++ types[i],
++ attrs_info_idx,
++ i);
++ SAFE_FREE(types);
++ code = EINVAL;
++ goto done;
++ }
++ attrs_info_idx = i;
++ break;
++ case PAC_TYPE_REQUESTER_SID:
++ if (requester_sid_idx != -1) {
++ DBG_WARNING("requester sid type[%u] twice"
++ "[%zd] and [%zu]: \n",
++ types[i],
++ requester_sid_idx,
++ i);
++ SAFE_FREE(types);
++ code = EINVAL;
++ goto done;
++ }
++ requester_sid_idx = i;
++ break;
+ default:
+ continue;
+ }
+@@ -766,6 +820,13 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
+ code = EINVAL;
+ goto done;
+ }
++ if (!(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) &&
++ requester_sid_idx == -1) {
++ DEBUG(1, ("PAC_TYPE_REQUESTER_SID missing\n"));
++ SAFE_FREE(types);
++ code = KRB5KDC_ERR_TGT_REVOKED;
++ goto done;
++ }
+
+ /* Build an updated PAC */
+ code = krb5_pac_init(context, &new_pac);
+@@ -831,6 +892,10 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
+ }
+ break;
+ case PAC_TYPE_SRV_CHECKSUM:
++ if (requester_sid_idx == -1 && requester_sid_blob != NULL) {
++ /* inject REQUESTER_SID */
++ forced_next_type = PAC_TYPE_REQUESTER_SID;
++ }
+ /*
+ * This is generated in the main KDC code
+ */
+@@ -840,6 +905,26 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
+ * This is generated in the main KDC code
+ */
+ continue;
++ case PAC_TYPE_ATTRIBUTES_INFO:
++ if (!is_untrusted && is_krbtgt) {
++ /* just copy... */
++ break;
++ }
++
++ continue;
++ case PAC_TYPE_REQUESTER_SID:
++ if (!is_krbtgt) {
++ continue;
++ }
++
++ /*
++ * Replace in the RODC case, otherwise
++ * requester_sid_blob is NULL and we just copy.
++ */
++ if (requester_sid_blob != NULL) {
++ type_blob = *requester_sid_blob;
++ }
++ break;
+ default:
+ /* just copy... */
+ break;
+diff --git source4/kdc/mit_samba.h source4/kdc/mit_samba.h
+index 636c77ec97c..4431e82a1b2 100644
+--- source4/kdc/mit_samba.h
++++ source4/kdc/mit_samba.h
+@@ -50,6 +50,7 @@ int mit_samba_get_nextkey(struct mit_samba_context *ctx,
+ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
+ krb5_context context,
+ krb5_db_entry *client,
++ krb5_db_entry *server,
+ krb5_keyblock *client_key,
+ krb5_pac *pac);
+
+--
+2.25.1
+
+
+From 6843c44a45044808f90687f85183e7111a465d1f Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 16 Jun 2022 10:33:29 +1200
+Subject: [PATCH 56/99] heimdal:kdc: Accommodate NULL data parameter in
+ krb5_pac_get_buffer()
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ source4/heimdal/lib/krb5/pac.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git source4/heimdal/lib/krb5/pac.c source4/heimdal/lib/krb5/pac.c
+index 05bcc523080..100de904662 100644
+--- source4/heimdal/lib/krb5/pac.c
++++ source4/heimdal/lib/krb5/pac.c
+@@ -394,10 +394,12 @@ krb5_pac_get_buffer(krb5_context context, krb5_pac p,
+ if (p->pac->buffers[i].type != type)
+ continue;
+
+- ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len);
+- if (ret) {
+- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+- return ret;
++ if (data) {
++ ret = krb5_data_copy(data, (unsigned char *)p->data.data + offset, len);
++ if (ret) {
++ krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
++ return ret;
++ }
+ }
+ return 0;
+ }
+--
+2.25.1
+
+
+From 1b38a28bcaebdae0128518605a422a194747a60f Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Fri, 27 May 2022 19:17:02 +1200
+Subject: [PATCH 57/99] CVE-2022-2031 s4:kpasswd: Account for missing target
+ principal
+
+This field is supposed to be optional.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ source4/kdc/kpasswd-service-mit.c | 22 ++++++++++++----------
+ 1 file changed, 12 insertions(+), 10 deletions(-)
+
+diff --git source4/kdc/kpasswd-service-mit.c source4/kdc/kpasswd-service-mit.c
+index 2117c1c1696..b53c1a4618a 100644
+--- source4/kdc/kpasswd-service-mit.c
++++ source4/kdc/kpasswd-service-mit.c
+@@ -143,16 +143,18 @@ static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
+ return KRB5_KPASSWD_HARDERROR;
+ }
+
+- target_realm = smb_krb5_principal_get_realm(
+- mem_ctx, context, target_principal);
+- code = krb5_unparse_name_flags(context,
+- target_principal,
+- KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+- &target_name);
+- if (code != 0) {
+- DBG_WARNING("Failed to parse principal\n");
+- *error_string = "String conversion failed";
+- return KRB5_KPASSWD_HARDERROR;
++ if (target_principal != NULL) {
++ target_realm = smb_krb5_principal_get_realm(
++ mem_ctx, context, target_principal);
++ code = krb5_unparse_name_flags(context,
++ target_principal,
++ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
++ &target_name);
++ if (code != 0) {
++ DBG_WARNING("Failed to parse principal\n");
++ *error_string = "String conversion failed";
++ return KRB5_KPASSWD_HARDERROR;
++ }
+ }
+
+ if ((target_name != NULL && target_realm == NULL) ||
+--
+2.25.1
+
+
+From f6c5a60336de8fd67a2ef371dd2ee4cf75c53904 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Mon, 30 May 2022 19:17:41 +1200
+Subject: [PATCH 58/99] CVE-2022-2031 s4:kpasswd: Add MIT fallback for decoding
+ setpw structure
+
+The target principal and realm fields of the setpw structure are
+supposed to be optional, but in MIT Kerberos they are mandatory. For
+better compatibility and ease of testing, fall back to parsing the
+simpler (containing only the new password) structure if the MIT function
+fails to decode it.
+
+Although the target principal and realm fields should be optional, one
+is not supposed to specified without the other, so we don't have to deal
+with the case where only one is specified.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ source4/kdc/kpasswd-service-mit.c | 94 ++++++++++++++++++++++++++-----
+ 1 file changed, 79 insertions(+), 15 deletions(-)
+
+diff --git source4/kdc/kpasswd-service-mit.c source4/kdc/kpasswd-service-mit.c
+index b53c1a4618a..9c4d2801669 100644
+--- source4/kdc/kpasswd-service-mit.c
++++ source4/kdc/kpasswd-service-mit.c
+@@ -28,6 +28,7 @@
+ #include "kdc/kpasswd_glue.h"
+ #include "kdc/kpasswd-service.h"
+ #include "kdc/kpasswd-helper.h"
++#include "../lib/util/asn1.h"
+
+ #define RFC3244_VERSION 0xff80
+
+@@ -35,6 +36,52 @@ krb5_error_code decode_krb5_setpw_req(const krb5_data *code,
+ krb5_data **password_out,
+ krb5_principal *target_out);
+
++/*
++ * A fallback for when MIT refuses to parse a setpw structure without the
++ * (optional) target principal and realm
++ */
++static bool decode_krb5_setpw_req_simple(TALLOC_CTX *mem_ctx,
++ const DATA_BLOB *decoded_data,
++ DATA_BLOB *clear_data)
++{
++ struct asn1_data *asn1 = NULL;
++ bool ret;
++
++ asn1 = asn1_init(mem_ctx, 3);
++ if (asn1 == NULL) {
++ return false;
++ }
++
++ ret = asn1_load(asn1, *decoded_data);
++ if (!ret) {
++ goto out;
++ }
++
++ ret = asn1_start_tag(asn1, ASN1_SEQUENCE(0));
++ if (!ret) {
++ goto out;
++ }
++ ret = asn1_start_tag(asn1, ASN1_CONTEXT(0));
++ if (!ret) {
++ goto out;
++ }
++ ret = asn1_read_OctetString(asn1, mem_ctx, clear_data);
++ if (!ret) {
++ goto out;
++ }
++
++ ret = asn1_end_tag(asn1);
++ if (!ret) {
++ goto out;
++ }
++ ret = asn1_end_tag(asn1);
++
++out:
++ asn1_free(asn1);
++
++ return ret;
++}
++
+ static krb5_error_code kpasswd_change_password(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
+ struct auth_session_info *session_info,
+@@ -93,9 +140,10 @@ static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
+ const char **error_string)
+ {
+ krb5_context context = kdc->smb_krb5_context->krb5_context;
++ DATA_BLOB clear_data;
+ krb5_data k_dec_data;
+- krb5_data *k_clear_data;
+- krb5_principal target_principal;
++ krb5_data *k_clear_data = NULL;
++ krb5_principal target_principal = NULL;
+ krb5_error_code code;
+ DATA_BLOB password;
+ char *target_realm = NULL;
+@@ -114,29 +162,45 @@ static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
+ code = decode_krb5_setpw_req(&k_dec_data,
+ &k_clear_data,
+ &target_principal);
+- if (code != 0) {
+- DBG_WARNING("decode_krb5_setpw_req failed: %s\n",
+- error_message(code));
+- ok = kpasswd_make_error_reply(mem_ctx,
+- KRB5_KPASSWD_MALFORMED,
+- "Failed to decode packet",
+- kpasswd_reply);
++ if (code == 0) {
++ clear_data.data = (uint8_t *)k_clear_data->data;
++ clear_data.length = k_clear_data->length;
++ } else {
++ target_principal = NULL;
++
++ /*
++ * The MIT decode failed, so fall back to trying the simple
++ * case, without target_principal.
++ */
++ ok = decode_krb5_setpw_req_simple(mem_ctx,
++ decoded_data,
++ &clear_data);
+ if (!ok) {
+- *error_string = "Failed to create reply";
+- return KRB5_KPASSWD_HARDERROR;
++ DBG_WARNING("decode_krb5_setpw_req failed: %s\n",
++ error_message(code));
++ ok = kpasswd_make_error_reply(mem_ctx,
++ KRB5_KPASSWD_MALFORMED,
++ "Failed to decode packet",
++ kpasswd_reply);
++ if (!ok) {
++ *error_string = "Failed to create reply";
++ return KRB5_KPASSWD_HARDERROR;
++ }
++ return 0;
+ }
+- return 0;
+ }
+
+ ok = convert_string_talloc_handle(mem_ctx,
+ lpcfg_iconv_handle(kdc->task->lp_ctx),
+ CH_UTF8,
+ CH_UTF16,
+- (const char *)k_clear_data->data,
+- k_clear_data->length,
++ clear_data.data,
++ clear_data.length,
+ (void **)&password.data,
+ &password.length);
+- krb5_free_data(context, k_clear_data);
++ if (k_clear_data != NULL) {
++ krb5_free_data(context, k_clear_data);
++ }
+ if (!ok) {
+ DBG_WARNING("String conversion failed\n");
+ *error_string = "String conversion failed";
+--
+2.25.1
+
+
+From 6305a55870287191ce4268f6af7fe278ca7f2a30 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 26 May 2022 16:34:01 +1200
+Subject: [PATCH 59/99] CVE-2022-32744 tests/krb5: Correctly handle specifying
+ account kvno
+
+The environment variable is a string, but we expect an integer.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ python/samba/tests/krb5/raw_testcase.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git python/samba/tests/krb5/raw_testcase.py python/samba/tests/krb5/raw_testcase.py
+index a2241707d44..4120edf93b9 100644
+--- python/samba/tests/krb5/raw_testcase.py
++++ python/samba/tests/krb5/raw_testcase.py
+@@ -724,7 +724,7 @@ class RawKerberosTest(TestCaseInTempDir):
+ fallback_default=False,
+ allow_missing=kvno_allow_missing)
+ if kvno is not None:
+- c.set_kvno(kvno)
++ c.set_kvno(int(kvno))
+ aes256_key = self.env_get_var('AES256_KEY_HEX', prefix,
+ fallback_default=False,
+ allow_missing=aes256_allow_missing)
+--
+2.25.1
+
+
+From 8917979641abb03ef858ba72b652178475b6e918 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 26 May 2022 20:52:04 +1200
+Subject: [PATCH 60/99] CVE-2022-2031 tests/krb5: Split out _make_tgs_request()
+
+This allows us to make use of it in other tests.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Fixed conflicts due to having older version of
+ _make_tgs_request()]
+---
+ python/samba/tests/krb5/kdc_base_test.py | 77 ++++++++++++++++++++++++
+ python/samba/tests/krb5/kdc_tgs_tests.py | 76 -----------------------
+ 2 files changed, 77 insertions(+), 76 deletions(-)
+
+diff --git python/samba/tests/krb5/kdc_base_test.py python/samba/tests/krb5/kdc_base_test.py
+index 58b87eab25b..2117663b26b 100644
+--- python/samba/tests/krb5/kdc_base_test.py
++++ python/samba/tests/krb5/kdc_base_test.py
+@@ -67,6 +67,7 @@ from samba.tests.krb5.rfc4120_constants import (
+ AES256_CTS_HMAC_SHA1_96,
+ ARCFOUR_HMAC_MD5,
+ KDC_ERR_PREAUTH_REQUIRED,
++ KDC_ERR_TGT_REVOKED,
+ KRB_AS_REP,
+ KRB_TGS_REP,
+ KRB_ERROR,
+@@ -1538,6 +1539,82 @@ class KDCBaseTest(RawKerberosTest):
+
+ return ticket_creds
+
++ def _make_tgs_request(self, client_creds, service_creds, tgt,
++ pac_request=None, expect_pac=True,
++ expect_error=False,
++ expected_account_name=None,
++ expected_upn_name=None,
++ expected_sid=None):
++ client_account = client_creds.get_username()
++ cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
++ names=[client_account])
++
++ service_account = service_creds.get_username()
++ sname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
++ names=[service_account])
++
++ realm = service_creds.get_realm()
++
++ expected_crealm = realm
++ expected_cname = cname
++ expected_srealm = realm
++ expected_sname = sname
++
++ expected_supported_etypes = service_creds.tgs_supported_enctypes
++
++ etypes = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
++
++ kdc_options = str(krb5_asn1.KDCOptions('canonicalize'))
++
++ target_decryption_key = self.TicketDecryptionKey_from_creds(
++ service_creds)
++
++ authenticator_subkey = self.RandomKey(kcrypto.Enctype.AES256)
++
++ if expect_error:
++ expected_error_mode = KDC_ERR_TGT_REVOKED
++ check_error_fn = self.generic_check_kdc_error
++ check_rep_fn = None
++ else:
++ expected_error_mode = 0
++ check_error_fn = None
++ check_rep_fn = self.generic_check_kdc_rep
++
++ kdc_exchange_dict = self.tgs_exchange_dict(
++ expected_crealm=expected_crealm,
++ expected_cname=expected_cname,
++ expected_srealm=expected_srealm,
++ expected_sname=expected_sname,
++ expected_account_name=expected_account_name,
++ expected_upn_name=expected_upn_name,
++ expected_sid=expected_sid,
++ expected_supported_etypes=expected_supported_etypes,
++ ticket_decryption_key=target_decryption_key,
++ check_error_fn=check_error_fn,
++ check_rep_fn=check_rep_fn,
++ check_kdc_private_fn=self.generic_check_kdc_private,
++ expected_error_mode=expected_error_mode,
++ tgt=tgt,
++ authenticator_subkey=authenticator_subkey,
++ kdc_options=kdc_options,
++ pac_request=pac_request,
++ expect_pac=expect_pac,
++ expect_edata=False)
++
++ rep = self._generic_kdc_exchange(kdc_exchange_dict,
++ cname=cname,
++ realm=realm,
++ sname=sname,
++ etypes=etypes)
++ if expect_error:
++ self.check_error_rep(rep, expected_error_mode)
++
++ return None
++ else:
++ self.check_reply(rep, KRB_TGS_REP)
++
++ return kdc_exchange_dict['rep_ticket_creds']
++
+ # Named tuple to contain values of interest when the PAC is decoded.
+ PacData = namedtuple(
+ "PacData",
+diff --git python/samba/tests/krb5/kdc_tgs_tests.py python/samba/tests/krb5/kdc_tgs_tests.py
+index 8cd27dec2aa..e52f46152fa 100755
+--- python/samba/tests/krb5/kdc_tgs_tests.py
++++ python/samba/tests/krb5/kdc_tgs_tests.py
+@@ -230,82 +230,6 @@ class KdcTgsTests(KDCBaseTest):
+ pac_data.account_sid,
+ "rep = {%s},%s" % (rep, pac_data))
+
+- def _make_tgs_request(self, client_creds, service_creds, tgt,
+- pac_request=None, expect_pac=True,
+- expect_error=False,
+- expected_account_name=None,
+- expected_upn_name=None,
+- expected_sid=None):
+- client_account = client_creds.get_username()
+- cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
+- names=[client_account])
+-
+- service_account = service_creds.get_username()
+- sname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
+- names=[service_account])
+-
+- realm = service_creds.get_realm()
+-
+- expected_crealm = realm
+- expected_cname = cname
+- expected_srealm = realm
+- expected_sname = sname
+-
+- expected_supported_etypes = service_creds.tgs_supported_enctypes
+-
+- etypes = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
+-
+- kdc_options = str(krb5_asn1.KDCOptions('canonicalize'))
+-
+- target_decryption_key = self.TicketDecryptionKey_from_creds(
+- service_creds)
+-
+- authenticator_subkey = self.RandomKey(kcrypto.Enctype.AES256)
+-
+- if expect_error:
+- expected_error_mode = KDC_ERR_TGT_REVOKED
+- check_error_fn = self.generic_check_kdc_error
+- check_rep_fn = None
+- else:
+- expected_error_mode = 0
+- check_error_fn = None
+- check_rep_fn = self.generic_check_kdc_rep
+-
+- kdc_exchange_dict = self.tgs_exchange_dict(
+- expected_crealm=expected_crealm,
+- expected_cname=expected_cname,
+- expected_srealm=expected_srealm,
+- expected_sname=expected_sname,
+- expected_account_name=expected_account_name,
+- expected_upn_name=expected_upn_name,
+- expected_sid=expected_sid,
+- expected_supported_etypes=expected_supported_etypes,
+- ticket_decryption_key=target_decryption_key,
+- check_error_fn=check_error_fn,
+- check_rep_fn=check_rep_fn,
+- check_kdc_private_fn=self.generic_check_kdc_private,
+- expected_error_mode=expected_error_mode,
+- tgt=tgt,
+- authenticator_subkey=authenticator_subkey,
+- kdc_options=kdc_options,
+- pac_request=pac_request,
+- expect_pac=expect_pac,
+- expect_edata=False)
+-
+- rep = self._generic_kdc_exchange(kdc_exchange_dict,
+- cname=cname,
+- realm=realm,
+- sname=sname,
+- etypes=etypes)
+- if expect_error:
+- self.check_error_rep(rep, expected_error_mode)
+-
+- return None
+- else:
+- self.check_reply(rep, KRB_TGS_REP)
+-
+- return kdc_exchange_dict['rep_ticket_creds']
+-
+ def test_request(self):
+ client_creds = self.get_client_creds()
+ service_creds = self.get_service_creds()
+--
+2.25.1
+
+
+From 245d9a42329a1bfeb3db8431ef105e7758080e14 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 24 May 2022 19:06:53 +1200
+Subject: [PATCH 61/99] CVE-2022-32744 tests/krb5: Correctly calculate salt for
+ pre-existing accounts
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ python/samba/tests/krb5/kdc_base_test.py | 1 +
+ python/samba/tests/krb5/raw_testcase.py | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git python/samba/tests/krb5/kdc_base_test.py python/samba/tests/krb5/kdc_base_test.py
+index 2117663b26b..685a6f71f88 100644
+--- python/samba/tests/krb5/kdc_base_test.py
++++ python/samba/tests/krb5/kdc_base_test.py
+@@ -1048,6 +1048,7 @@ class KDCBaseTest(RawKerberosTest):
+
+ kvno = int(res[0]['msDS-KeyVersionNumber'][0])
+ creds.set_kvno(kvno)
++ creds.set_workstation(username[:-1])
+ creds.set_dn(dn)
+
+ keys = self.get_keys(samdb, dn)
+diff --git python/samba/tests/krb5/raw_testcase.py python/samba/tests/krb5/raw_testcase.py
+index 4120edf93b9..a9a98c36cbf 100644
+--- python/samba/tests/krb5/raw_testcase.py
++++ python/samba/tests/krb5/raw_testcase.py
+@@ -834,6 +834,7 @@ class RawKerberosTest(TestCaseInTempDir):
+ allow_missing_password=allow_missing_password,
+ allow_missing_keys=allow_missing_keys)
+ c.set_gensec_features(c.get_gensec_features() | FEATURE_SEAL)
++ c.set_workstation('')
+ return c
+
+ def get_rodc_krbtgt_creds(self,
+--
+2.25.1
+
+
+From f7fad997cc06a14c9ffd101b26e16598f334148b Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 24 May 2022 19:13:54 +1200
+Subject: [PATCH 62/99] CVE-2022-2031 tests/krb5: Add new definitions for
+ kpasswd
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ python/samba/tests/krb5/rfc4120.asn1 | 6 ++++++
+ python/samba/tests/krb5/rfc4120_constants.py | 13 +++++++++++++
+ python/samba/tests/krb5/rfc4120_pyasn1.py | 13 ++++++++++++-
+ 3 files changed, 31 insertions(+), 1 deletion(-)
+
+diff --git python/samba/tests/krb5/rfc4120.asn1 python/samba/tests/krb5/rfc4120.asn1
+index e0831e1f86f..cac884be985 100644
+--- python/samba/tests/krb5/rfc4120.asn1
++++ python/samba/tests/krb5/rfc4120.asn1
+@@ -567,6 +567,12 @@ PA-FX-FAST-REPLY ::= CHOICE {
+ ...
+ }
+
++ChangePasswdDataMS ::= SEQUENCE {
++ newpasswd [0] OCTET STRING,
++ targname [1] PrincipalName OPTIONAL,
++ targrealm [2] Realm OPTIONAL
++}
++
+ -- MS-KILE End
+ --
+ --
+diff --git python/samba/tests/krb5/rfc4120_constants.py python/samba/tests/krb5/rfc4120_constants.py
+index a9fdc5735dd..7f0f44500c7 100644
+--- python/samba/tests/krb5/rfc4120_constants.py
++++ python/samba/tests/krb5/rfc4120_constants.py
+@@ -27,11 +27,13 @@ ARCFOUR_HMAC_MD5 = int(
+
+ # Message types
+ KRB_ERROR = int(krb5_asn1.MessageTypeValues('krb-error'))
++KRB_AP_REP = int(krb5_asn1.MessageTypeValues('krb-ap-rep'))
+ KRB_AP_REQ = int(krb5_asn1.MessageTypeValues('krb-ap-req'))
+ KRB_AS_REP = int(krb5_asn1.MessageTypeValues('krb-as-rep'))
+ KRB_AS_REQ = int(krb5_asn1.MessageTypeValues('krb-as-req'))
+ KRB_TGS_REP = int(krb5_asn1.MessageTypeValues('krb-tgs-rep'))
+ KRB_TGS_REQ = int(krb5_asn1.MessageTypeValues('krb-tgs-req'))
++KRB_PRIV = int(krb5_asn1.MessageTypeValues('krb-priv'))
+
+ # PAData types
+ PADATA_ENC_TIMESTAMP = int(
+@@ -76,6 +78,7 @@ KDC_ERR_TGT_REVOKED = 20
+ KDC_ERR_PREAUTH_FAILED = 24
+ KDC_ERR_PREAUTH_REQUIRED = 25
+ KDC_ERR_BAD_INTEGRITY = 31
++KDC_ERR_TKT_EXPIRED = 32
+ KRB_ERR_TKT_NYV = 33
+ KDC_ERR_NOT_US = 35
+ KDC_ERR_BADMATCH = 36
+@@ -87,6 +90,16 @@ KDC_ERR_WRONG_REALM = 68
+ KDC_ERR_CLIENT_NAME_MISMATCH = 75
+ KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS = 93
+
++# Kpasswd error codes
++KPASSWD_SUCCESS = 0
++KPASSWD_MALFORMED = 1
++KPASSWD_HARDERROR = 2
++KPASSWD_AUTHERROR = 3
++KPASSWD_SOFTERROR = 4
++KPASSWD_ACCESSDENIED = 5
++KPASSWD_BAD_VERSION = 6
++KPASSWD_INITIAL_FLAG_NEEDED = 7
++
+ # Extended error types
+ KERB_AP_ERR_TYPE_SKEW_RECOVERY = int(
+ krb5_asn1.KerbErrorDataTypeValues('kERB-AP-ERR-TYPE-SKEW-RECOVERY'))
+diff --git python/samba/tests/krb5/rfc4120_pyasn1.py python/samba/tests/krb5/rfc4120_pyasn1.py
+index 348dd8c63fb..3c02b0efbc1 100644
+--- python/samba/tests/krb5/rfc4120_pyasn1.py
++++ python/samba/tests/krb5/rfc4120_pyasn1.py
+@@ -1,5 +1,5 @@
+ # Auto-generated by asn1ate v.0.6.1.dev0 from rfc4120.asn1
+-# (last modified on 2021-06-25 12:10:34.484667)
++# (last modified on 2022-05-13 20:03:06.039817)
+
+ # KerberosV5Spec2
+ from pyasn1.type import univ, char, namedtype, namedval, tag, constraint, useful
+@@ -364,6 +364,17 @@ Authenticator.componentType = namedtype.NamedTypes(
+ )
+
+
++class ChangePasswdDataMS(univ.Sequence):
++ pass
++
++
++ChangePasswdDataMS.componentType = namedtype.NamedTypes(
++ namedtype.NamedType('newpasswd', univ.OctetString().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 0))),
++ namedtype.OptionalNamedType('targname', PrincipalName().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatConstructed, 1))),
++ namedtype.OptionalNamedType('targrealm', Realm().subtype(explicitTag=tag.Tag(tag.tagClassContext, tag.tagFormatSimple, 2)))
++)
++
++
+ class ChecksumTypeValues(univ.Integer):
+ pass
+
+--
+2.25.1
+
+
+From 695c662bdc286d7a4699025f00656f8339ceecd8 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 24 May 2022 19:17:45 +1200
+Subject: [PATCH 63/99] CVE-2022-2031 tests/krb5: Add methods to create ASN1
+ kpasswd structures
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ python/samba/tests/krb5/raw_testcase.py | 95 +++++++++++++++++++++++++
+ 1 file changed, 95 insertions(+)
+
+diff --git python/samba/tests/krb5/raw_testcase.py python/samba/tests/krb5/raw_testcase.py
+index a9a98c36cbf..df41dff688d 100644
+--- python/samba/tests/krb5/raw_testcase.py
++++ python/samba/tests/krb5/raw_testcase.py
+@@ -54,6 +54,7 @@ from samba.tests.krb5.rfc4120_constants import (
+ KRB_AS_REP,
+ KRB_AS_REQ,
+ KRB_ERROR,
++ KRB_PRIV,
+ KRB_TGS_REP,
+ KRB_TGS_REQ,
+ KU_AP_REQ_AUTH,
+@@ -63,6 +64,7 @@ from samba.tests.krb5.rfc4120_constants import (
+ KU_FAST_FINISHED,
+ KU_FAST_REP,
+ KU_FAST_REQ_CHKSUM,
++ KU_KRB_PRIV,
+ KU_NON_KERB_CKSUM_SALT,
+ KU_TGS_REP_ENC_PART_SESSION,
+ KU_TGS_REP_ENC_PART_SUB_KEY,
+@@ -1780,6 +1782,99 @@ class RawKerberosTest(TestCaseInTempDir):
+ PA_S4U2Self_obj, asn1Spec=krb5_asn1.PA_S4U2Self())
+ return self.PA_DATA_create(PADATA_FOR_USER, pa_s4u2self)
+
++ def ChangePasswdDataMS_create(self,
++ new_password,
++ target_princ=None,
++ target_realm=None):
++ ChangePasswdDataMS_obj = {
++ 'newpasswd': new_password,
++ }
++ if target_princ is not None:
++ ChangePasswdDataMS_obj['targname'] = target_princ
++ if target_realm is not None:
++ ChangePasswdDataMS_obj['targrealm'] = target_realm
++
++ change_password_data = self.der_encode(
++ ChangePasswdDataMS_obj, asn1Spec=krb5_asn1.ChangePasswdDataMS())
++
++ return change_password_data
++
++ def KRB_PRIV_create(self,
++ subkey,
++ user_data,
++ s_address,
++ timestamp=None,
++ usec=None,
++ seq_number=None,
++ r_address=None):
++ EncKrbPrivPart_obj = {
++ 'user-data': user_data,
++ 's-address': s_address,
++ }
++ if timestamp is not None:
++ EncKrbPrivPart_obj['timestamp'] = timestamp
++ if usec is not None:
++ EncKrbPrivPart_obj['usec'] = usec
++ if seq_number is not None:
++ EncKrbPrivPart_obj['seq-number'] = seq_number
++ if r_address is not None:
++ EncKrbPrivPart_obj['r-address'] = r_address
++
++ enc_krb_priv_part = self.der_encode(
++ EncKrbPrivPart_obj, asn1Spec=krb5_asn1.EncKrbPrivPart())
++
++ enc_data = self.EncryptedData_create(subkey,
++ KU_KRB_PRIV,
++ enc_krb_priv_part)
++
++ KRB_PRIV_obj = {
++ 'pvno': 5,
++ 'msg-type': KRB_PRIV,
++ 'enc-part': enc_data,
++ }
++
++ krb_priv = self.der_encode(
++ KRB_PRIV_obj, asn1Spec=krb5_asn1.KRB_PRIV())
++
++ return krb_priv
++
++ def kpasswd_create(self,
++ subkey,
++ user_data,
++ version,
++ seq_number,
++ ap_req,
++ local_address,
++ remote_address):
++ self.assertIsNotNone(self.s, 'call self.connect() first')
++
++ timestamp, usec = self.get_KerberosTimeWithUsec()
++
++ krb_priv = self.KRB_PRIV_create(subkey,
++ user_data,
++ s_address=local_address,
++ timestamp=timestamp,
++ usec=usec,
++ seq_number=seq_number,
++ r_address=remote_address)
++
++ size = 6 + len(ap_req) + len(krb_priv)
++ self.assertLess(size, 0x10000)
++
++ msg = bytearray()
++ msg.append(size >> 8)
++ msg.append(size & 0xff)
++ msg.append(version >> 8)
++ msg.append(version & 0xff)
++ msg.append(len(ap_req) >> 8)
++ msg.append(len(ap_req) & 0xff)
++ # Note: for sets, there could be a little-endian four-byte length here.
++
++ msg.extend(ap_req)
++ msg.extend(krb_priv)
++
++ return msg
++
+ def _generic_kdc_exchange(self,
+ kdc_exchange_dict, # required
+ cname=None, # optional
+--
+2.25.1
+
+
+From ae7dd875cd4362ed4346716db493164c421b889f Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 24 May 2022 19:21:37 +1200
+Subject: [PATCH 64/99] CVE-2022-2031 tests/krb5: Add 'port' parameter to
+ connect()
+
+This allows us to use the kpasswd port, 464.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ python/samba/tests/krb5/raw_testcase.py | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git python/samba/tests/krb5/raw_testcase.py python/samba/tests/krb5/raw_testcase.py
+index df41dff688d..421143781ae 100644
+--- python/samba/tests/krb5/raw_testcase.py
++++ python/samba/tests/krb5/raw_testcase.py
+@@ -638,10 +638,11 @@ class RawKerberosTest(TestCaseInTempDir):
+ if self.do_hexdump:
+ sys.stderr.write("disconnect[%s]\n" % reason)
+
+- def _connect_tcp(self, host):
+- tcp_port = 88
++ def _connect_tcp(self, host, port=None):
++ if port is None:
++ port = 88
+ try:
+- self.a = socket.getaddrinfo(host, tcp_port, socket.AF_UNSPEC,
++ self.a = socket.getaddrinfo(host, port, socket.AF_UNSPEC,
+ socket.SOCK_STREAM, socket.SOL_TCP,
+ 0)
+ self.s = socket.socket(self.a[0][0], self.a[0][1], self.a[0][2])
+@@ -654,9 +655,9 @@ class RawKerberosTest(TestCaseInTempDir):
+ self.s.close()
+ raise
+
+- def connect(self, host):
++ def connect(self, host, port=None):
+ self.assertNotConnected()
+- self._connect_tcp(host)
++ self._connect_tcp(host, port)
+ if self.do_hexdump:
+ sys.stderr.write("connected[%s]\n" % host)
+
+--
+2.25.1
+
+
+From 13fe7e013eccca2c86258084f4443ddb7abaf089 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 24 May 2022 19:20:28 +1200
+Subject: [PATCH 65/99] CVE-2022-2031 tests/krb5: Add methods to send and
+ receive generic messages
+
+This allows us to send and receive kpasswd messages, while avoiding the
+existing logic for encoding and decoding other Kerberos message types.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ python/samba/tests/krb5/raw_testcase.py | 44 +++++++++++++++----------
+ 1 file changed, 27 insertions(+), 17 deletions(-)
+
+diff --git python/samba/tests/krb5/raw_testcase.py python/samba/tests/krb5/raw_testcase.py
+index 421143781ae..2aed5530455 100644
+--- python/samba/tests/krb5/raw_testcase.py
++++ python/samba/tests/krb5/raw_testcase.py
+@@ -920,24 +920,28 @@ class RawKerberosTest(TestCaseInTempDir):
+ return blob
+
+ def send_pdu(self, req, asn1_print=None, hexdump=None):
++ k5_pdu = self.der_encode(
++ req, native_decode=False, asn1_print=asn1_print, hexdump=False)
++ self.send_msg(k5_pdu, hexdump=hexdump)
++
++ def send_msg(self, msg, hexdump=None):
++ header = struct.pack('>I', len(msg))
++ req_pdu = header
++ req_pdu += msg
++ self.hex_dump("send_msg", header, hexdump=hexdump)
++ self.hex_dump("send_msg", msg, hexdump=hexdump)
++
+ try:
+- k5_pdu = self.der_encode(
+- req, native_decode=False, asn1_print=asn1_print, hexdump=False)
+- header = struct.pack('>I', len(k5_pdu))
+- req_pdu = header
+- req_pdu += k5_pdu
+- self.hex_dump("send_pdu", header, hexdump=hexdump)
+- self.hex_dump("send_pdu", k5_pdu, hexdump=hexdump)
+ while True:
+ sent = self.s.send(req_pdu, 0)
+ if sent == len(req_pdu):
+- break
++ return
+ req_pdu = req_pdu[sent:]
+ except socket.error as e:
+- self._disconnect("send_pdu: %s" % e)
++ self._disconnect("send_msg: %s" % e)
+ raise
+ except IOError as e:
+- self._disconnect("send_pdu: %s" % e)
++ self._disconnect("send_msg: %s" % e)
+ raise
+
+ def recv_raw(self, num_recv=0xffff, hexdump=None, timeout=None):
+@@ -963,16 +967,14 @@ class RawKerberosTest(TestCaseInTempDir):
+ return rep_pdu
+
+ def recv_pdu_raw(self, asn1_print=None, hexdump=None, timeout=None):
+- rep_pdu = None
+- rep = None
+ raw_pdu = self.recv_raw(
+ num_recv=4, hexdump=hexdump, timeout=timeout)
+ if raw_pdu is None:
+- return (None, None)
++ return None
+ header = struct.unpack(">I", raw_pdu[0:4])
+ k5_len = header[0]
+ if k5_len == 0:
+- return (None, "")
++ return ""
+ missing = k5_len
+ rep_pdu = b''
+ while missing > 0:
+@@ -981,6 +983,14 @@ class RawKerberosTest(TestCaseInTempDir):
+ self.assertGreaterEqual(len(raw_pdu), 1)
+ rep_pdu += raw_pdu
+ missing = k5_len - len(rep_pdu)
++ return rep_pdu
++
++ def recv_reply(self, asn1_print=None, hexdump=None, timeout=None):
++ rep_pdu = self.recv_pdu_raw(asn1_print=asn1_print,
++ hexdump=hexdump,
++ timeout=timeout)
++ if not rep_pdu:
++ return None, rep_pdu
+ k5_raw = self.der_decode(
+ rep_pdu,
+ asn1Spec=None,
+@@ -1002,9 +1012,9 @@ class RawKerberosTest(TestCaseInTempDir):
+ return (rep, rep_pdu)
+
+ def recv_pdu(self, asn1_print=None, hexdump=None, timeout=None):
+- (rep, rep_pdu) = self.recv_pdu_raw(asn1_print=asn1_print,
+- hexdump=hexdump,
+- timeout=timeout)
++ (rep, rep_pdu) = self.recv_reply(asn1_print=asn1_print,
++ hexdump=hexdump,
++ timeout=timeout)
+ return rep
+
+ def assertIsConnected(self):
+--
+2.25.1
+
+
+From ca582250fcaf2ad3c585f7e31a1a4ce568b7ddb7 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 24 May 2022 19:26:56 +1200
+Subject: [PATCH 66/99] tests/krb5: Fix enum typo
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ python/samba/tests/krb5/kdc_base_test.py | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git python/samba/tests/krb5/kdc_base_test.py python/samba/tests/krb5/kdc_base_test.py
+index 685a6f71f88..14f1d1a243d 100644
+--- python/samba/tests/krb5/kdc_base_test.py
++++ python/samba/tests/krb5/kdc_base_test.py
+@@ -248,9 +248,9 @@ class KDCBaseTest(RawKerberosTest):
+ which is used by tearDownClass to clean up the created accounts.
+ '''
+ if ou is None:
+- if account_type is account_type.COMPUTER:
++ if account_type is self.AccountType.COMPUTER:
+ guid = DS_GUID_COMPUTERS_CONTAINER
+- elif account_type is account_type.SERVER:
++ elif account_type is self.AccountType.SERVER:
+ guid = DS_GUID_DOMAIN_CONTROLLERS_CONTAINER
+ else:
+ guid = DS_GUID_USERS_CONTAINER
+--
+2.25.1
+
+
+From 5b030b176b853938b1895ec255e838147d8e7fa9 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 24 May 2022 19:30:12 +1200
+Subject: [PATCH 67/99] tests/krb5: Add option for creating accounts with
+ expired passwords
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ python/samba/tests/krb5/kdc_base_test.py | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git python/samba/tests/krb5/kdc_base_test.py python/samba/tests/krb5/kdc_base_test.py
+index 14f1d1a243d..777b3b4aaf1 100644
+--- python/samba/tests/krb5/kdc_base_test.py
++++ python/samba/tests/krb5/kdc_base_test.py
+@@ -242,7 +242,8 @@ class KDCBaseTest(RawKerberosTest):
+
+ def create_account(self, samdb, name, account_type=AccountType.USER,
+ spn=None, upn=None, additional_details=None,
+- ou=None, account_control=0, add_dollar=True):
++ ou=None, account_control=0, add_dollar=True,
++ expired_password=False):
+ '''Create an account for testing.
+ The dn of the created account is added to self.accounts,
+ which is used by tearDownClass to clean up the created accounts.
+@@ -294,6 +295,8 @@ class KDCBaseTest(RawKerberosTest):
+ details["servicePrincipalName"] = spn
+ if upn is not None:
+ details["userPrincipalName"] = upn
++ if expired_password:
++ details["pwdLastSet"] = "0"
+ if additional_details is not None:
+ details.update(additional_details)
+ samdb.add(details)
+@@ -653,6 +656,7 @@ class KDCBaseTest(RawKerberosTest):
+ 'revealed_to_rodc': False,
+ 'revealed_to_mock_rodc': False,
+ 'no_auth_data_required': False,
++ 'expired_password': False,
+ 'supported_enctypes': None,
+ 'not_delegated': False,
+ 'delegation_to_spn': None,
+@@ -695,6 +699,7 @@ class KDCBaseTest(RawKerberosTest):
+ revealed_to_rodc,
+ revealed_to_mock_rodc,
+ no_auth_data_required,
++ expired_password,
+ supported_enctypes,
+ not_delegated,
+ delegation_to_spn,
+@@ -754,7 +759,8 @@ class KDCBaseTest(RawKerberosTest):
+ spn=spn,
+ additional_details=details,
+ account_control=user_account_control,
+- add_dollar=add_dollar)
++ add_dollar=add_dollar,
++ expired_password=expired_password)
+
+ keys = self.get_keys(samdb, dn)
+ self.creds_set_keys(creds, keys)
+--
+2.25.1
+
+
+From 5c41e20fae268e04aa05e821c7f388ea090727af Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 24 May 2022 19:34:59 +1200
+Subject: [PATCH 68/99] CVE-2022-2031 tests/krb5: Allow requesting a TGT to a
+ different sname and realm
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Fixed conflict due to lacking rc4_support parameter]
+
+[jsutton@samba.org Fixed conflicts due to lacking client_name_type and
+ expected_cname parameters]
+---
+ python/samba/tests/krb5/kdc_base_test.py | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+diff --git python/samba/tests/krb5/kdc_base_test.py python/samba/tests/krb5/kdc_base_test.py
+index 777b3b4aaf1..c0ca881985a 100644
+--- python/samba/tests/krb5/kdc_base_test.py
++++ python/samba/tests/krb5/kdc_base_test.py
+@@ -1344,10 +1344,12 @@ class KDCBaseTest(RawKerberosTest):
+ expected_flags=None, unexpected_flags=None,
+ pac_request=True, expect_pac=True, fresh=False):
+ user_name = tgt.cname['name-string'][0]
++ ticket_sname = tgt.sname
+ if target_name is None:
+ target_name = target_creds.get_username()[:-1]
+ cache_key = (user_name, target_name, service, to_rodc, kdc_options,
+ pac_request, str(expected_flags), str(unexpected_flags),
++ str(ticket_sname),
+ expect_pac)
+
+ if not fresh:
+@@ -1414,6 +1416,7 @@ class KDCBaseTest(RawKerberosTest):
+ expected_flags=None, unexpected_flags=None,
+ expected_account_name=None, expected_upn_name=None,
+ expected_sid=None,
++ sname=None, realm=None,
+ pac_request=True, expect_pac=True,
+ expect_pac_attrs=None, expect_pac_attrs_pac_request=None,
+ expect_requester_sid=None,
+@@ -1422,6 +1425,7 @@ class KDCBaseTest(RawKerberosTest):
+ cache_key = (user_name, to_rodc, kdc_options, pac_request,
+ str(expected_flags), str(unexpected_flags),
+ expected_account_name, expected_upn_name, expected_sid,
++ str(sname), str(realm),
+ expect_pac, expect_pac_attrs,
+ expect_pac_attrs_pac_request, expect_requester_sid)
+
+@@ -1431,15 +1435,21 @@ class KDCBaseTest(RawKerberosTest):
+ if tgt is not None:
+ return tgt
+
+- realm = creds.get_realm()
++ if realm is None:
++ realm = creds.get_realm()
+
+ salt = creds.get_salt()
+
+ etype = (AES256_CTS_HMAC_SHA1_96, ARCFOUR_HMAC_MD5)
+ cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
+ names=[user_name])
+- sname = self.PrincipalName_create(name_type=NT_SRV_INST,
+- names=['krbtgt', realm])
++ if sname is None:
++ sname = self.PrincipalName_create(name_type=NT_SRV_INST,
++ names=['krbtgt', realm])
++ expected_sname = self.PrincipalName_create(
++ name_type=NT_SRV_INST, names=['krbtgt', realm.upper()])
++ else:
++ expected_sname = sname
+
+ till = self.get_KerberosTime(offset=36000)
+
+@@ -1505,9 +1515,6 @@ class KDCBaseTest(RawKerberosTest):
+
+ expected_realm = realm.upper()
+
+- expected_sname = self.PrincipalName_create(
+- name_type=NT_SRV_INST, names=['krbtgt', realm.upper()])
+-
+ rep, kdc_exchange_dict = self._test_as_exchange(
+ cname=cname,
+ realm=realm,
+--
+2.25.1
+
+
+From 668825ad56ff70715c626bc3209a6868409e4969 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 24 May 2022 19:57:57 +1200
+Subject: [PATCH 69/99] CVE-2022-2031 tests/krb5: Add kpasswd_exchange() method
+
+Now we can test the kpasswd service from Python.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Fixed conflicts in imports]
+---
+ python/samba/tests/krb5/raw_testcase.py | 264 ++++++++++++++++++++++--
+ 1 file changed, 251 insertions(+), 13 deletions(-)
+
+diff --git python/samba/tests/krb5/raw_testcase.py python/samba/tests/krb5/raw_testcase.py
+index 2aed5530455..57010ae73bd 100644
+--- python/samba/tests/krb5/raw_testcase.py
++++ python/samba/tests/krb5/raw_testcase.py
+@@ -26,6 +26,8 @@ import binascii
+ import itertools
+ import collections
+
++from enum import Enum
++
+ from pyasn1.codec.der.decoder import decode as pyasn1_der_decode
+ from pyasn1.codec.der.encoder import encode as pyasn1_der_encode
+ from pyasn1.codec.native.decoder import decode as pyasn1_native_decode
+@@ -33,6 +35,8 @@ from pyasn1.codec.native.encoder import encode as pyasn1_native_encode
+
+ from pyasn1.codec.ber.encoder import BitStringEncoder
+
++from pyasn1.error import PyAsn1Error
++
+ from samba.credentials import Credentials
+ from samba.dcerpc import krb5pac, security
+ from samba.gensec import FEATURE_SEAL
+@@ -50,6 +54,7 @@ from samba.tests.krb5.rfc4120_constants import (
+ KDC_ERR_PREAUTH_FAILED,
+ KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS,
+ KERB_ERR_TYPE_EXTENDED,
++ KRB_AP_REP,
+ KRB_AP_REQ,
+ KRB_AS_REP,
+ KRB_AS_REQ,
+@@ -59,6 +64,7 @@ from samba.tests.krb5.rfc4120_constants import (
+ KRB_TGS_REQ,
+ KU_AP_REQ_AUTH,
+ KU_AS_REP_ENC_PART,
++ KU_AP_REQ_ENC_PART,
+ KU_ENC_CHALLENGE_KDC,
+ KU_FAST_ENC,
+ KU_FAST_FINISHED,
+@@ -73,6 +79,7 @@ from samba.tests.krb5.rfc4120_constants import (
+ KU_TGS_REQ_AUTH_DAT_SESSION,
+ KU_TGS_REQ_AUTH_DAT_SUBKEY,
+ KU_TICKET,
++ NT_PRINCIPAL,
+ NT_SRV_INST,
+ NT_WELLKNOWN,
+ PADATA_ENCRYPTED_CHALLENGE,
+@@ -515,6 +522,10 @@ class KerberosTicketCreds:
+ class RawKerberosTest(TestCaseInTempDir):
+ """A raw Kerberos Test case."""
+
++ class KpasswdMode(Enum):
++ SET = object()
++ CHANGE = object()
++
+ pac_checksum_types = {krb5pac.PAC_TYPE_SRV_CHECKSUM,
+ krb5pac.PAC_TYPE_KDC_CHECKSUM,
+ krb5pac.PAC_TYPE_TICKET_CHECKSUM}
+@@ -1886,6 +1897,224 @@ class RawKerberosTest(TestCaseInTempDir):
+
+ return msg
+
++ def get_enc_part(self, obj, key, usage):
++ self.assertElementEqual(obj, 'pvno', 5)
++
++ enc_part = obj['enc-part']
++ self.assertElementEqual(enc_part, 'etype', key.etype)
++ self.assertElementKVNO(enc_part, 'kvno', key.kvno)
++
++ enc_part = key.decrypt(usage, enc_part['cipher'])
++
++ return enc_part
++
++ def kpasswd_exchange(self,
++ ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode,
++ target_princ=None,
++ target_realm=None,
++ ap_options=None,
++ send_seq_number=True):
++ if mode is self.KpasswdMode.SET:
++ version = 0xff80
++ user_data = self.ChangePasswdDataMS_create(new_password,
++ target_princ,
++ target_realm)
++ elif mode is self.KpasswdMode.CHANGE:
++ self.assertIsNone(target_princ,
++ 'target_princ only valid for pw set')
++ self.assertIsNone(target_realm,
++ 'target_realm only valid for pw set')
++
++ version = 1
++ user_data = new_password.encode('utf-8')
++ else:
++ self.fail(f'invalid mode {mode}')
++
++ subkey = self.RandomKey(kcrypto.Enctype.AES256)
++
++ if ap_options is None:
++ ap_options = '0'
++ ap_options = str(krb5_asn1.APOptions(ap_options))
++
++ kdc_exchange_dict = {
++ 'tgt': ticket,
++ 'authenticator_subkey': subkey,
++ 'auth_data': None,
++ 'ap_options': ap_options,
++ }
++
++ if send_seq_number:
++ seq_number = random.randint(0, 0xfffffffe)
++ else:
++ seq_number = None
++
++ ap_req = self.generate_ap_req(kdc_exchange_dict,
++ None,
++ req_body=None,
++ armor=False,
++ usage=KU_AP_REQ_AUTH,
++ seq_number=seq_number)
++
++ self.connect(self.host, port=464)
++ self.assertIsNotNone(self.s)
++
++ family = self.s.family
++
++ if family == socket.AF_INET:
++ addr_type = 2 # IPv4
++ elif family == socket.AF_INET6:
++ addr_type = 24 # IPv6
++ else:
++ self.fail(f'unknown family {family}')
++
++ def create_address(ip):
++ return {
++ 'addr-type': addr_type,
++ 'address': socket.inet_pton(family, ip),
++ }
++
++ local_ip = self.s.getsockname()[0]
++ local_address = create_address(local_ip)
++
++ # remote_ip = self.s.getpeername()[0]
++ # remote_address = create_address(remote_ip)
++
++ # TODO: due to a bug (?), MIT Kerberos will not accept the request
++ # unless r-address is set to our _local_ address. Heimdal, on the other
++ # hand, requires the r-address is set to the remote address (as
++ # expected). To avoid problems, avoid sending r-address for now.
++ remote_address = None
++
++ msg = self.kpasswd_create(subkey,
++ user_data,
++ version,
++ seq_number,
++ ap_req,
++ local_address,
++ remote_address)
++
++ self.send_msg(msg)
++ rep_pdu = self.recv_pdu_raw()
++
++ self._disconnect('transaction done')
++
++ self.assertIsNotNone(rep_pdu)
++
++ header = rep_pdu[:6]
++ reply = rep_pdu[6:]
++
++ reply_len = (header[0] << 8) | header[1]
++ reply_version = (header[2] << 8) | header[3]
++ ap_rep_len = (header[4] << 8) | header[5]
++
++ self.assertEqual(reply_len, len(rep_pdu))
++ self.assertEqual(1, reply_version) # KRB5_KPASSWD_VERS_CHANGEPW
++ self.assertLess(ap_rep_len, reply_len)
++
++ self.assertNotEqual(0x7e, rep_pdu[1])
++ self.assertNotEqual(0x5e, rep_pdu[1])
++
++ if ap_rep_len:
++ # We received an AP-REQ and KRB-PRIV as a response. This may or may
++ # not indicate an error, depending on the status code.
++ ap_rep = reply[:ap_rep_len]
++ krb_priv = reply[ap_rep_len:]
++
++ key = ticket.session_key
++
++ ap_rep = self.der_decode(ap_rep, asn1Spec=krb5_asn1.AP_REP())
++ self.assertElementEqual(ap_rep, 'msg-type', KRB_AP_REP)
++ enc_part = self.get_enc_part(ap_rep, key, KU_AP_REQ_ENC_PART)
++ enc_part = self.der_decode(
++ enc_part, asn1Spec=krb5_asn1.EncAPRepPart())
++
++ self.assertElementPresent(enc_part, 'ctime')
++ self.assertElementPresent(enc_part, 'cusec')
++ # self.assertElementMissing(enc_part, 'subkey') # TODO
++ # self.assertElementPresent(enc_part, 'seq-number') # TODO
++
++ try:
++ krb_priv = self.der_decode(krb_priv, asn1Spec=krb5_asn1.KRB_PRIV())
++ except PyAsn1Error:
++ self.fail()
++
++ self.assertElementEqual(krb_priv, 'msg-type', KRB_PRIV)
++ priv_enc_part = self.get_enc_part(krb_priv, subkey, KU_KRB_PRIV)
++ priv_enc_part = self.der_decode(
++ priv_enc_part, asn1Spec=krb5_asn1.EncKrbPrivPart())
++
++ self.assertElementMissing(priv_enc_part, 'timestamp')
++ self.assertElementMissing(priv_enc_part, 'usec')
++ # self.assertElementPresent(priv_enc_part, 'seq-number') # TODO
++ # self.assertElementEqual(priv_enc_part, 's-address', remote_address) # TODO
++ # self.assertElementMissing(priv_enc_part, 'r-address') # TODO
++
++ result_data = priv_enc_part['user-data']
++ else:
++ # We received a KRB-ERROR as a response, indicating an error.
++ krb_error = self.der_decode(reply, asn1Spec=krb5_asn1.KRB_ERROR())
++
++ sname = self.PrincipalName_create(
++ name_type=NT_PRINCIPAL,
++ names=['kadmin', 'changepw'])
++ realm = self.get_krbtgt_creds().get_realm().upper()
++
++ self.assertElementEqual(krb_error, 'pvno', 5)
++ self.assertElementEqual(krb_error, 'msg-type', KRB_ERROR)
++ self.assertElementMissing(krb_error, 'ctime')
++ self.assertElementMissing(krb_error, 'usec')
++ self.assertElementPresent(krb_error, 'stime')
++ self.assertElementPresent(krb_error, 'susec')
++
++ error_code = krb_error['error-code']
++ if isinstance(expected_code, int):
++ self.assertEqual(error_code, expected_code)
++ else:
++ self.assertIn(error_code, expected_code)
++
++ self.assertElementMissing(krb_error, 'crealm')
++ self.assertElementMissing(krb_error, 'cname')
++ self.assertElementEqual(krb_error, 'realm', realm.encode('utf-8'))
++ self.assertElementEqualPrincipal(krb_error, 'sname', sname)
++ self.assertElementMissing(krb_error, 'e-text')
++
++ result_data = krb_error['e-data']
++
++ status = result_data[:2]
++ message = result_data[2:]
++
++ status_code = (status[0] << 8) | status[1]
++ if isinstance(expected_code, int):
++ self.assertEqual(status_code, expected_code)
++ else:
++ self.assertIn(status_code, expected_code)
++
++ if not message:
++ self.assertEqual(0, status_code,
++ 'got an error result, but no message')
++ return
++
++ # Check the first character of the message.
++ if message[0]:
++ if isinstance(expected_msg, bytes):
++ self.assertEqual(message, expected_msg)
++ else:
++ self.assertIn(message, expected_msg)
++ else:
++ # We got AD password policy information.
++ self.assertEqual(30, len(message))
++
++ (empty_bytes,
++ min_length,
++ history_length,
++ properties,
++ expire_time,
++ min_age) = struct.unpack('>HIIIQQ', message)
++
+ def _generic_kdc_exchange(self,
+ kdc_exchange_dict, # required
+ cname=None, # optional
+@@ -1996,7 +2225,7 @@ class RawKerberosTest(TestCaseInTempDir):
+ self.assertIsNotNone(generate_fast_fn)
+ fast_ap_req = generate_fast_armor_fn(kdc_exchange_dict,
+ callback_dict,
+- req_body,
++ None,
+ armor=True)
+
+ fast_armor_type = kdc_exchange_dict['fast_armor_type']
+@@ -3211,31 +3440,39 @@ class RawKerberosTest(TestCaseInTempDir):
+ kdc_exchange_dict,
+ _callback_dict,
+ req_body,
+- armor):
++ armor,
++ usage=None,
++ seq_number=None):
++ req_body_checksum = None
++
+ if armor:
++ self.assertIsNone(req_body)
++
+ tgt = kdc_exchange_dict['armor_tgt']
+ authenticator_subkey = kdc_exchange_dict['armor_subkey']
+-
+- req_body_checksum = None
+ else:
+ tgt = kdc_exchange_dict['tgt']
+ authenticator_subkey = kdc_exchange_dict['authenticator_subkey']
+- body_checksum_type = kdc_exchange_dict['body_checksum_type']
+
+- req_body_blob = self.der_encode(req_body,
+- asn1Spec=krb5_asn1.KDC_REQ_BODY())
++ if req_body is not None:
++ body_checksum_type = kdc_exchange_dict['body_checksum_type']
++
++ req_body_blob = self.der_encode(
++ req_body, asn1Spec=krb5_asn1.KDC_REQ_BODY())
+
+- req_body_checksum = self.Checksum_create(tgt.session_key,
+- KU_TGS_REQ_AUTH_CKSUM,
+- req_body_blob,
+- ctype=body_checksum_type)
++ req_body_checksum = self.Checksum_create(
++ tgt.session_key,
++ KU_TGS_REQ_AUTH_CKSUM,
++ req_body_blob,
++ ctype=body_checksum_type)
+
+ auth_data = kdc_exchange_dict['auth_data']
+
+ subkey_obj = None
+ if authenticator_subkey is not None:
+ subkey_obj = authenticator_subkey.export_obj()
+- seq_number = random.randint(0, 0xfffffffe)
++ if seq_number is None:
++ seq_number = random.randint(0, 0xfffffffe)
+ (ctime, cusec) = self.get_KerberosTimeWithUsec()
+ authenticator_obj = self.Authenticator_create(
+ crealm=tgt.crealm,
+@@ -3250,7 +3487,8 @@ class RawKerberosTest(TestCaseInTempDir):
+ authenticator_obj,
+ asn1Spec=krb5_asn1.Authenticator())
+
+- usage = KU_AP_REQ_AUTH if armor else KU_TGS_REQ_AUTH
++ if usage is None:
++ usage = KU_AP_REQ_AUTH if armor else KU_TGS_REQ_AUTH
+ authenticator = self.EncryptedData_create(tgt.session_key,
+ usage,
+ authenticator_blob)
+--
+2.25.1
+
+
+From 450ff39d1c9f538bd828b7b2bee75c88d3dc1ee2 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 24 May 2022 19:59:16 +1200
+Subject: [PATCH 71/99] CVE-2022-2031 tests/krb5: Add tests for kpasswd service
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Fixed conflicts in usage.py and knownfails; removed
+ MIT KDC 1.20-specific knownfails as it's not supported]
+
+[jsutton@samba.org Fixed conflicts in usage.py, knownfails, and
+ tests.py]
+---
+ python/samba/tests/krb5/kdc_base_test.py | 4 +-
+ python/samba/tests/krb5/kpasswd_tests.py | 1021 ++++++++++++++++++++++
+ python/samba/tests/krb5/raw_testcase.py | 8 +
+ python/samba/tests/usage.py | 1 +
+ selftest/knownfail_heimdal_kdc | 26 +
+ selftest/knownfail_mit_kdc | 26 +
+ source4/selftest/tests.py | 4 +
+ 7 files changed, 1089 insertions(+), 1 deletion(-)
+ create mode 100755 python/samba/tests/krb5/kpasswd_tests.py
+
+diff --git python/samba/tests/krb5/kdc_base_test.py python/samba/tests/krb5/kdc_base_test.py
+index c0ca881985a..f0306dde110 100644
+--- python/samba/tests/krb5/kdc_base_test.py
++++ python/samba/tests/krb5/kdc_base_test.py
+@@ -1586,7 +1586,9 @@ class KDCBaseTest(RawKerberosTest):
+ authenticator_subkey = self.RandomKey(kcrypto.Enctype.AES256)
+
+ if expect_error:
+- expected_error_mode = KDC_ERR_TGT_REVOKED
++ expected_error_mode = expect_error
++ if expected_error_mode is True:
++ expected_error_mode = KDC_ERR_TGT_REVOKED
+ check_error_fn = self.generic_check_kdc_error
+ check_rep_fn = None
+ else:
+diff --git python/samba/tests/krb5/kpasswd_tests.py python/samba/tests/krb5/kpasswd_tests.py
+new file mode 100755
+index 00000000000..3a6c7d818dc
+--- /dev/null
++++ python/samba/tests/krb5/kpasswd_tests.py
+@@ -0,0 +1,1021 @@
++#!/usr/bin/env python3
++# Unix SMB/CIFS implementation.
++# Copyright (C) Stefan Metzmacher 2020
++# Copyright (C) Catalyst.Net Ltd
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# the Free Software Foundation; either version 3 of the License, or
++# (at your option) any later version.
++#
++# This program is distributed in the hope that it will be useful,
++# but WITHOUT ANY WARRANTY; without even the implied warranty of
++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++# GNU General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program. If not, see <http://www.gnu.org/licenses/>.
++#
++
++import os
++import sys
++
++from functools import partial
++
++from samba import generate_random_password, unix2nttime
++from samba.dcerpc import krb5pac, security
++from samba.sd_utils import SDUtils
++
++from samba.tests.krb5.kdc_base_test import KDCBaseTest
++from samba.tests.krb5.rfc4120_constants import (
++ KDC_ERR_TGT_REVOKED,
++ KDC_ERR_TKT_EXPIRED,
++ KPASSWD_ACCESSDENIED,
++ KPASSWD_HARDERROR,
++ KPASSWD_INITIAL_FLAG_NEEDED,
++ KPASSWD_MALFORMED,
++ KPASSWD_SOFTERROR,
++ KPASSWD_SUCCESS,
++ NT_PRINCIPAL,
++ NT_SRV_INST,
++)
++
++sys.path.insert(0, 'bin/python')
++os.environ['PYTHONUNBUFFERED'] = '1'
++
++global_asn1_print = False
++global_hexdump = False
++
++
++# Note: these tests do not pass on Windows, which returns different error codes
++# to the ones we have chosen, and does not always return additional error data.
++class KpasswdTests(KDCBaseTest):
++
++ def setUp(self):
++ super().setUp()
++ self.do_asn1_print = global_asn1_print
++ self.do_hexdump = global_hexdump
++
++ samdb = self.get_samdb()
++
++ # Get the old 'dSHeuristics' if it was set
++ dsheuristics = samdb.get_dsheuristics()
++
++ # Reset the 'dSHeuristics' as they were before
++ self.addCleanup(samdb.set_dsheuristics, dsheuristics)
++
++ # Set the 'dSHeuristics' to activate the correct 'userPassword'
++ # behaviour
++ samdb.set_dsheuristics('000000001')
++
++ # Get the old 'minPwdAge'
++ minPwdAge = samdb.get_minPwdAge()
++
++ # Reset the 'minPwdAge' as it was before
++ self.addCleanup(samdb.set_minPwdAge, minPwdAge)
++
++ # Set it temporarily to '0'
++ samdb.set_minPwdAge('0')
++
++ def _get_creds(self, expired=False):
++ opts = {
++ 'expired_password': expired
++ }
++
++ # Create the account.
++ creds = self.get_cached_creds(account_type=self.AccountType.USER,
++ opts=opts,
++ use_cache=False)
++
++ return creds
++
++ def issued_by_rodc(self, ticket):
++ krbtgt_creds = self.get_mock_rodc_krbtgt_creds()
++
++ krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds)
++ checksum_keys = {
++ krb5pac.PAC_TYPE_KDC_CHECKSUM: krbtgt_key,
++ }
++
++ return self.modified_ticket(
++ ticket,
++ new_ticket_key=krbtgt_key,
++ checksum_keys=checksum_keys)
++
++ def get_kpasswd_sname(self):
++ return self.PrincipalName_create(name_type=NT_PRINCIPAL,
++ names=['kadmin', 'changepw'])
++
++ def get_ticket_lifetime(self, ticket):
++ enc_part = ticket.ticket_private
++
++ authtime = enc_part['authtime']
++ starttime = enc_part.get('starttime', authtime)
++ endtime = enc_part['endtime']
++
++ starttime = self.get_EpochFromKerberosTime(starttime)
++ endtime = self.get_EpochFromKerberosTime(endtime)
++
++ return endtime - starttime
++
++ def add_requester_sid(self, pac, sid):
++ pac_buffers = pac.buffers
++
++ buffer_types = [pac_buffer.type for pac_buffer in pac_buffers]
++ self.assertNotIn(krb5pac.PAC_TYPE_REQUESTER_SID, buffer_types)
++
++ requester_sid = krb5pac.PAC_REQUESTER_SID()
++ requester_sid.sid = security.dom_sid(sid)
++
++ requester_sid_buffer = krb5pac.PAC_BUFFER()
++ requester_sid_buffer.type = krb5pac.PAC_TYPE_REQUESTER_SID
++ requester_sid_buffer.info = requester_sid
++
++ pac_buffers.append(requester_sid_buffer)
++
++ pac.buffers = pac_buffers
++ pac.num_buffers += 1
++
++ return pac
++
++ # Test setting a password with kpasswd.
++ def test_kpasswd_set(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ expected_code = KPASSWD_SUCCESS
++ expected_msg = b'Password changed'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ # Test the newly set password.
++ creds.update_password(new_password)
++ self.get_tgt(creds, fresh=True)
++
++ # Test changing a password with kpasswd.
++ def test_kpasswd_change(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ expected_code = KPASSWD_SUCCESS
++ expected_msg = b'Password changed'
++
++ # Change the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Test the newly set password.
++ creds.update_password(new_password)
++ self.get_tgt(creds, fresh=True)
++
++ # Test kpasswd without setting the canonicalize option.
++ def test_kpasswd_no_canonicalize(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ sname = self.get_kpasswd_sname()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=sname,
++ kdc_options='0')
++
++ expected_code = KPASSWD_SUCCESS
++ expected_msg = b'Password changed'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ creds.update_password(new_password)
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=sname,
++ kdc_options='0')
++
++ # Change the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Test kpasswd with the canonicalize option reset and a non-canonical
++ # (by conversion to title case) realm.
++ def test_kpasswd_no_canonicalize_realm_case(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ sname = self.get_kpasswd_sname()
++ realm = creds.get_realm().capitalize() # We use a title-cased realm.
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=sname,
++ realm=realm,
++ kdc_options='0')
++
++ expected_code = KPASSWD_SUCCESS
++ expected_msg = b'Password changed'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ creds.update_password(new_password)
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=sname,
++ realm=realm,
++ kdc_options='0')
++
++ # Change the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Test kpasswd with the canonicalize option set.
++ def test_kpasswd_canonicalize(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ # Get an initial ticket to kpasswd. We set the canonicalize flag here.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='canonicalize')
++
++ expected_code = KPASSWD_SUCCESS
++ expected_msg = b'Password changed'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ creds.update_password(new_password)
++
++ # Get an initial ticket to kpasswd. We set the canonicalize flag here.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='canonicalize')
++
++ # Change the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Test kpasswd with the canonicalize option set and a non-canonical (by
++ # conversion to title case) realm.
++ def test_kpasswd_canonicalize_realm_case(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ sname = self.get_kpasswd_sname()
++ realm = creds.get_realm().capitalize() # We use a title-cased realm.
++
++ # Get an initial ticket to kpasswd. We set the canonicalize flag here.
++ ticket = self.get_tgt(creds, sname=sname,
++ realm=realm,
++ kdc_options='canonicalize')
++
++ expected_code = KPASSWD_SUCCESS
++ expected_msg = b'Password changed'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ creds.update_password(new_password)
++
++ # Get an initial ticket to kpasswd. We set the canonicalize flag here.
++ ticket = self.get_tgt(creds, sname=sname,
++ realm=realm,
++ kdc_options='canonicalize')
++
++ # Change the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Test kpasswd rejects a password that does not meet complexity
++ # requirements.
++ def test_kpasswd_too_weak(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ expected_code = KPASSWD_SOFTERROR
++ expected_msg = b'Password does not meet complexity requirements'
++
++ # Set the password.
++ new_password = 'password'
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ # Change the password.
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Test kpasswd rejects an empty new password.
++ def test_kpasswd_empty(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ expected_code = KPASSWD_SOFTERROR, KPASSWD_HARDERROR
++ expected_msg = (b'Password too short, password must be at least 7 '
++ b'characters long.',
++ b'String conversion failed!')
++
++ # Set the password.
++ new_password = ''
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ expected_code = KPASSWD_HARDERROR
++ expected_msg = b'String conversion failed!'
++
++ # Change the password.
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Test kpasswd rejects a request that does not include a random sequence
++ # number.
++ def test_kpasswd_no_seq_number(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ expected_code = KPASSWD_HARDERROR
++ expected_msg = b'gensec_unwrap failed - NT_STATUS_ACCESS_DENIED\n'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET,
++ send_seq_number=False)
++
++ # Change the password.
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE,
++ send_seq_number=False)
++
++ # Test kpasswd rejects a ticket issued by an RODC.
++ def test_kpasswd_from_rodc(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ # Have the ticket be issued by the RODC.
++ ticket = self.issued_by_rodc(ticket)
++
++ expected_code = KPASSWD_HARDERROR
++ expected_msg = b'gensec_update failed - NT_STATUS_LOGON_FAILURE\n'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ # Change the password.
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Test setting a password, specifying the principal of the target user.
++ def test_kpasswd_set_target_princ_only(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++ username = creds.get_username()
++
++ cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
++ names=username.split('/'))
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ expected_code = KPASSWD_MALFORMED
++ expected_msg = (b'Realm and principal must be both present, or '
++ b'neither present',
++ b'Failed to decode packet')
++
++ # Change the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET,
++ target_princ=cname)
++
++ # Test that kpasswd rejects a password set specifying only the realm of the
++ # target user.
++ def test_kpasswd_set_target_realm_only(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ expected_code = KPASSWD_MALFORMED, KPASSWD_ACCESSDENIED
++ expected_msg = (b'Realm and principal must be both present, or '
++ b'neither present',
++ b'Failed to decode packet',
++ b'No such user when changing password')
++
++ # Change the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET,
++ target_realm=creds.get_realm())
++
++ # Show that a user cannot set a password, specifying both principal and
++ # realm of the target user, without having control access.
++ def test_kpasswd_set_target_princ_and_realm_no_access(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++ username = creds.get_username()
++
++ cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
++ names=username.split('/'))
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ expected_code = KPASSWD_ACCESSDENIED
++ expected_msg = b'Not permitted to change password'
++
++ # Change the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET,
++ target_princ=cname,
++ target_realm=creds.get_realm())
++
++ # Test setting a password, specifying both principal and realm of the
++ # target user, whem the user has control access on their account.
++ def test_kpasswd_set_target_princ_and_realm_access(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++ username = creds.get_username()
++ tgt = self.get_tgt(creds)
++
++ cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
++ names=username.split('/'))
++
++ samdb = self.get_samdb()
++ sd_utils = SDUtils(samdb)
++
++ user_dn = creds.get_dn()
++ user_sid = self.get_objectSid(samdb, user_dn)
++
++ # Give the user control access on their account.
++ ace = f'(A;;CR;;;{user_sid})'
++ sd_utils.dacl_add_ace(user_dn, ace)
++
++ # Get a non-initial ticket to kpasswd. Since we have the right to
++ # change the account's password, we don't need an initial ticket.
++ krbtgt_creds = self.get_krbtgt_creds()
++ ticket = self.get_service_ticket(tgt,
++ krbtgt_creds,
++ service='kadmin',
++ target_name='changepw',
++ kdc_options='0')
++
++ expected_code = KPASSWD_SUCCESS
++ expected_msg = b'Password changed'
++
++ # Change the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET,
++ target_princ=cname,
++ target_realm=creds.get_realm())
++
++ # Test setting a password when the existing password has expired.
++ def test_kpasswd_set_expired_password(self):
++ # Create an account for testing, with an expired password.
++ creds = self._get_creds(expired=True)
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ expected_code = KPASSWD_SUCCESS
++ expected_msg = b'Password changed'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ # Test changing a password when the existing password has expired.
++ def test_kpasswd_change_expired_password(self):
++ # Create an account for testing, with an expired password.
++ creds = self._get_creds(expired=True)
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ expected_code = KPASSWD_SUCCESS
++ expected_msg = b'Password changed'
++
++ # Change the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Check the lifetime of a kpasswd ticket is not more than two minutes.
++ def test_kpasswd_ticket_lifetime(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ # Check the lifetime of the ticket is equal to two minutes.
++ lifetime = self.get_ticket_lifetime(ticket)
++ self.assertEqual(2 * 60, lifetime)
++
++ # Ensure we cannot perform a TGS-REQ with a kpasswd ticket.
++ def test_kpasswd_ticket_tgs(self):
++ creds = self.get_client_creds()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ # Change the sname of the ticket to match that of a TGT.
++ realm = creds.get_realm()
++ krbtgt_sname = self.PrincipalName_create(name_type=NT_SRV_INST,
++ names=['krbtgt', realm])
++ ticket.set_sname(krbtgt_sname)
++
++ # Try to use that ticket to get a service ticket.
++ service_creds = self.get_service_creds()
++
++ # This fails due to missing REQUESTER_SID buffer.
++ self._make_tgs_request(creds, service_creds, ticket,
++ expect_error=(KDC_ERR_TGT_REVOKED,
++ KDC_ERR_TKT_EXPIRED))
++
++ def modify_requester_sid_time(self, ticket, sid, lifetime):
++ # Get the krbtgt key.
++ krbtgt_creds = self.get_krbtgt_creds()
++
++ krbtgt_key = self.TicketDecryptionKey_from_creds(krbtgt_creds)
++ checksum_keys = {
++ krb5pac.PAC_TYPE_KDC_CHECKSUM: krbtgt_key,
++ }
++
++ # Set authtime and starttime to an hour in the past, to show that they
++ # do not affect ticket rejection.
++ start_time = self.get_KerberosTime(offset=-60 * 60)
++
++ # Set the endtime of the ticket relative to our current time, so that
++ # the ticket has 'lifetime' seconds remaining to live.
++ end_time = self.get_KerberosTime(offset=lifetime)
++
++ # Modify the times in the ticket.
++ def modify_ticket_times(enc_part):
++ enc_part['authtime'] = start_time
++ if 'starttime' in enc_part:
++ enc_part['starttime'] = start_time
++
++ enc_part['endtime'] = end_time
++
++ return enc_part
++
++ # We have to set the times in both the ticket and the PAC, otherwise
++ # Heimdal will complain.
++ def modify_pac_time(pac):
++ pac_buffers = pac.buffers
++
++ for pac_buffer in pac_buffers:
++ if pac_buffer.type == krb5pac.PAC_TYPE_LOGON_NAME:
++ logon_time = self.get_EpochFromKerberosTime(start_time)
++ pac_buffer.info.logon_time = unix2nttime(logon_time)
++ break
++ else:
++ self.fail('failed to find LOGON_NAME PAC buffer')
++
++ pac.buffers = pac_buffers
++
++ return pac
++
++ # Add a requester SID to show that the KDC will then accept this
++ # kpasswd ticket as if it were a TGT.
++ def modify_pac_fn(pac):
++ pac = self.add_requester_sid(pac, sid=sid)
++ pac = modify_pac_time(pac)
++ return pac
++
++ # Do the actual modification.
++ return self.modified_ticket(ticket,
++ new_ticket_key=krbtgt_key,
++ modify_fn=modify_ticket_times,
++ modify_pac_fn=modify_pac_fn,
++ checksum_keys=checksum_keys)
++
++ # Ensure we cannot perform a TGS-REQ with a kpasswd ticket containing a
++ # requester SID and having a remaining lifetime of two minutes.
++ def test_kpasswd_ticket_requester_sid_tgs(self):
++ creds = self.get_client_creds()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ # Change the sname of the ticket to match that of a TGT.
++ realm = creds.get_realm()
++ krbtgt_sname = self.PrincipalName_create(name_type=NT_SRV_INST,
++ names=['krbtgt', realm])
++ ticket.set_sname(krbtgt_sname)
++
++ # Get the user's SID.
++ samdb = self.get_samdb()
++
++ user_dn = creds.get_dn()
++ user_sid = self.get_objectSid(samdb, user_dn)
++
++ # Modify the ticket to add a requester SID and give it two minutes to
++ # live.
++ ticket = self.modify_requester_sid_time(ticket,
++ sid=user_sid,
++ lifetime=2 * 60)
++
++ # Try to use that ticket to get a service ticket.
++ service_creds = self.get_service_creds()
++
++ # This fails due to the lifetime being too short.
++ self._make_tgs_request(creds, service_creds, ticket,
++ expect_error=KDC_ERR_TKT_EXPIRED)
++
++ # Show we can perform a TGS-REQ with a kpasswd ticket containing a
++ # requester SID if the remaining lifetime exceeds two minutes.
++ def test_kpasswd_ticket_requester_sid_lifetime_tgs(self):
++ creds = self.get_client_creds()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=self.get_kpasswd_sname(),
++ kdc_options='0')
++
++ # Change the sname of the ticket to match that of a TGT.
++ realm = creds.get_realm()
++ krbtgt_sname = self.PrincipalName_create(name_type=NT_SRV_INST,
++ names=['krbtgt', realm])
++ ticket.set_sname(krbtgt_sname)
++
++ # Get the user's SID.
++ samdb = self.get_samdb()
++
++ user_dn = creds.get_dn()
++ user_sid = self.get_objectSid(samdb, user_dn)
++
++ # Modify the ticket to add a requester SID and give it two minutes and
++ # ten seconds to live.
++ ticket = self.modify_requester_sid_time(ticket,
++ sid=user_sid,
++ lifetime=2 * 60 + 10)
++
++ # Try to use that ticket to get a service ticket.
++ service_creds = self.get_service_creds()
++
++ # This succeeds.
++ self._make_tgs_request(creds, service_creds, ticket,
++ expect_error=False)
++
++ # Test that kpasswd rejects requests with a service ticket.
++ def test_kpasswd_non_initial(self):
++ # Create an account for testing, and get a TGT.
++ creds = self._get_creds()
++ tgt = self.get_tgt(creds)
++
++ # Get a non-initial ticket to kpasswd.
++ krbtgt_creds = self.get_krbtgt_creds()
++ ticket = self.get_service_ticket(tgt,
++ krbtgt_creds,
++ service='kadmin',
++ target_name='changepw',
++ kdc_options='0')
++
++ expected_code = KPASSWD_INITIAL_FLAG_NEEDED
++ expected_msg = b'Expected an initial ticket'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ # Change the password.
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Show that kpasswd accepts requests with a service ticket modified to set
++ # the 'initial' flag.
++ def test_kpasswd_initial(self):
++ # Create an account for testing, and get a TGT.
++ creds = self._get_creds()
++
++ krbtgt_creds = self.get_krbtgt_creds()
++
++ # Get a service ticket, and modify it to set the 'initial' flag.
++ def get_ticket():
++ tgt = self.get_tgt(creds, fresh=True)
++
++ # Get a non-initial ticket to kpasswd.
++ ticket = self.get_service_ticket(tgt,
++ krbtgt_creds,
++ service='kadmin',
++ target_name='changepw',
++ kdc_options='0',
++ fresh=True)
++
++ set_initial_flag = partial(self.modify_ticket_flag, flag='initial',
++ value=True)
++
++ checksum_keys = self.get_krbtgt_checksum_key()
++ return self.modified_ticket(ticket,
++ modify_fn=set_initial_flag,
++ checksum_keys=checksum_keys)
++
++ expected_code = KPASSWD_SUCCESS
++ expected_msg = b'Password changed'
++
++ ticket = get_ticket()
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ creds.update_password(new_password)
++ ticket = get_ticket()
++
++ # Change the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Test that kpasswd rejects requests where the ticket is encrypted with a
++ # key other than the krbtgt's.
++ def test_kpasswd_wrong_key(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ sname = self.get_kpasswd_sname()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=sname,
++ kdc_options='0')
++
++ # Get a key belonging to the Administrator account.
++ admin_creds = self.get_admin_creds()
++ admin_key = self.TicketDecryptionKey_from_creds(admin_creds)
++ self.assertIsNotNone(admin_key.kvno,
++ 'a kvno is required to tell the DB '
++ 'which key to look up.')
++ checksum_keys = {
++ krb5pac.PAC_TYPE_KDC_CHECKSUM: admin_key,
++ }
++
++ # Re-encrypt the ticket using the Administrator's key.
++ ticket = self.modified_ticket(ticket,
++ new_ticket_key=admin_key,
++ checksum_keys=checksum_keys)
++
++ # Set the sname of the ticket to that of the Administrator account.
++ admin_sname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
++ names=['Administrator'])
++ ticket.set_sname(admin_sname)
++
++ expected_code = KPASSWD_HARDERROR
++ expected_msg = b'gensec_update failed - NT_STATUS_LOGON_FAILURE\n'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ # Change the password.
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ def test_kpasswd_wrong_key_service(self):
++ # Create an account for testing.
++ creds = self.get_cached_creds(account_type=self.AccountType.COMPUTER,
++ use_cache=False)
++
++ sname = self.get_kpasswd_sname()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=sname,
++ kdc_options='0')
++
++ # Get a key belonging to our account.
++ our_key = self.TicketDecryptionKey_from_creds(creds)
++ self.assertIsNotNone(our_key.kvno,
++ 'a kvno is required to tell the DB '
++ 'which key to look up.')
++ checksum_keys = {
++ krb5pac.PAC_TYPE_KDC_CHECKSUM: our_key,
++ }
++
++ # Re-encrypt the ticket using our key.
++ ticket = self.modified_ticket(ticket,
++ new_ticket_key=our_key,
++ checksum_keys=checksum_keys)
++
++ # Set the sname of the ticket to that of our account.
++ username = creds.get_username()
++ sname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
++ names=username.split('/'))
++ ticket.set_sname(sname)
++
++ expected_code = KPASSWD_HARDERROR
++ expected_msg = b'gensec_update failed - NT_STATUS_LOGON_FAILURE\n'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ # Change the password.
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++ # Test that kpasswd rejects requests where the ticket is encrypted with a
++ # key belonging to a server account other than the krbtgt.
++ def test_kpasswd_wrong_key_server(self):
++ # Create an account for testing.
++ creds = self._get_creds()
++
++ sname = self.get_kpasswd_sname()
++
++ # Get an initial ticket to kpasswd.
++ ticket = self.get_tgt(creds, sname=sname,
++ kdc_options='0')
++
++ # Get a key belonging to the DC's account.
++ dc_creds = self.get_dc_creds()
++ dc_key = self.TicketDecryptionKey_from_creds(dc_creds)
++ self.assertIsNotNone(dc_key.kvno,
++ 'a kvno is required to tell the DB '
++ 'which key to look up.')
++ checksum_keys = {
++ krb5pac.PAC_TYPE_KDC_CHECKSUM: dc_key,
++ }
++
++ # Re-encrypt the ticket using the DC's key.
++ ticket = self.modified_ticket(ticket,
++ new_ticket_key=dc_key,
++ checksum_keys=checksum_keys)
++
++ # Set the sname of the ticket to that of the DC's account.
++ dc_username = dc_creds.get_username()
++ dc_sname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
++ names=dc_username.split('/'))
++ ticket.set_sname(dc_sname)
++
++ expected_code = KPASSWD_HARDERROR
++ expected_msg = b'gensec_update failed - NT_STATUS_LOGON_FAILURE\n'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ # Change the password.
++ self.kpasswd_exchange(ticket,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
++
++if __name__ == '__main__':
++ global_asn1_print = False
++ global_hexdump = False
++ import unittest
++ unittest.main()
+diff --git python/samba/tests/krb5/raw_testcase.py python/samba/tests/krb5/raw_testcase.py
+index 57010ae73bd..4a78a8eadf3 100644
+--- python/samba/tests/krb5/raw_testcase.py
++++ python/samba/tests/krb5/raw_testcase.py
+@@ -500,6 +500,10 @@ class KerberosCredentials(Credentials):
+ def get_upn(self):
+ return self.upn
+
++ def update_password(self, password):
++ self.set_password(password)
++ self.set_kvno(self.get_kvno() + 1)
++
+
+ class KerberosTicketCreds:
+ def __init__(self, ticket, session_key,
+@@ -518,6 +522,10 @@ class KerberosTicketCreds:
+ self.ticket_private = ticket_private
+ self.encpart_private = encpart_private
+
++ def set_sname(self, sname):
++ self.ticket['sname'] = sname
++ self.sname = sname
++
+
+ class RawKerberosTest(TestCaseInTempDir):
+ """A raw Kerberos Test case."""
+diff --git python/samba/tests/usage.py python/samba/tests/usage.py
+index 6bbd96e7a08..a1210ada579 100644
+--- python/samba/tests/usage.py
++++ python/samba/tests/usage.py
+@@ -109,6 +109,7 @@ EXCLUDE_USAGE = {
+ 'python/samba/tests/krb5/alias_tests.py',
+ 'python/samba/tests/krb5/test_min_domain_uid.py',
+ 'python/samba/tests/krb5/test_idmap_nss.py',
++ 'python/samba/tests/krb5/kpasswd_tests.py',
+ }
+
+ EXCLUDE_HELP = {
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 424a8b81c38..54e69a48bc1 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -271,3 +271,29 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
++#
++# Kpasswd tests
++#
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_change.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_change_expired_password.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_empty.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_initial.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_seq_number.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_non_initial.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_expired_password.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_and_realm_access.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_and_realm_no_access.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_only.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_realm_only.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_too_weak.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 108c6055d0c..53638afc17a 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -575,3 +575,29 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_logon_info_sid_mismatch_nonexisting
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_tgs_rodc_requester_sid_mismatch_nonexisting
++#
++# Kpasswd tests
++#
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_change.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_change_expired_password.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_empty.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_initial.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_seq_number.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_non_initial.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_expired_password.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_and_realm_access.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_and_realm_no_access.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_only.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_realm_only.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_too_weak.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+--
+2.25.1
+
+
+From 29ec8b2369b5f5e2a660a3165d2528982514a0f2 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Fri, 27 May 2022 19:21:06 +1200
+Subject: [PATCH 72/99] CVE-2022-2031 s4:kpasswd: Correctly generate error
+ strings
+
+The error_data we create already has an explicit length, and should not
+be zero-terminated, so we omit the trailing null byte. Previously,
+Heimdal builds would leave a superfluous trailing null byte on error
+strings, while MIT builds would omit the final character.
+
+The two bytes added to the string's length are for the prepended error
+code.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Removed MIT KDC 1.20-specific knownfails]
+---
+ selftest/knownfail_heimdal_kdc | 12 ------------
+ selftest/knownfail_mit_kdc | 15 ---------------
+ source4/kdc/kpasswd-helper.c | 13 ++++++-------
+ 3 files changed, 6 insertions(+), 34 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 54e69a48bc1..40e24f3155b 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -276,24 +276,12 @@
+ #
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_change.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_change_expired_password.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_empty.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_initial.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_seq_number.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_non_initial.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_expired_password.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_and_realm_access.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_and_realm_no_access.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_only.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_realm_only.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_too_weak.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 53638afc17a..a914c4d3492 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -578,26 +578,11 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ #
+ # Kpasswd tests
+ #
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_change.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_change_expired_password.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_empty.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_initial.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_seq_number.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_non_initial.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_expired_password.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_and_realm_access.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_and_realm_no_access.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_princ_only.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_set_target_realm_only.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_too_weak.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+diff --git source4/kdc/kpasswd-helper.c source4/kdc/kpasswd-helper.c
+index 995f54825b5..55a2f5b3bf6 100644
+--- source4/kdc/kpasswd-helper.c
++++ source4/kdc/kpasswd-helper.c
+@@ -48,17 +48,16 @@ bool kpasswd_make_error_reply(TALLOC_CTX *mem_ctx,
+ }
+
+ /*
+- * The string 's' has two terminating nul-bytes which are also
+- * reflected by 'slen'. Normally Kerberos doesn't expect that strings
+- * are nul-terminated, but Heimdal does!
++ * The string 's' has one terminating nul-byte which is also
++ * reflected by 'slen'. We subtract it from the length.
+ */
+-#ifndef SAMBA4_USES_HEIMDAL
+- if (slen < 2) {
++ if (slen < 1) {
+ talloc_free(s);
+ return false;
+ }
+- slen -= 2;
+-#endif
++ slen--;
++
++ /* Two bytes are added to the length to account for the error code. */
+ if (2 + slen < slen) {
+ talloc_free(s);
+ return false;
+--
+2.25.1
+
+
+From 3a8da51396f3bf9d4caf8dbd4e75a0314aa47046 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 18 May 2022 16:48:59 +1200
+Subject: [PATCH 73/99] CVE-2022-2031 s4:kpasswd: Don't return AP-REP on
+ failure
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Removed MIT KDC 1.20-specific knownfails]
+---
+ selftest/knownfail_mit_kdc | 1 -
+ source4/kdc/kpasswd-service.c | 2 ++
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index a914c4d3492..f64291e776d 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -579,7 +579,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ # Kpasswd tests
+ #
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_empty.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_non_initial.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
+diff --git source4/kdc/kpasswd-service.c source4/kdc/kpasswd-service.c
+index 8f1679e4a28..a3c57a67dd1 100644
+--- source4/kdc/kpasswd-service.c
++++ source4/kdc/kpasswd-service.c
+@@ -253,6 +253,7 @@ kdc_code kpasswd_process(struct kdc_server *kdc,
+ &kpasswd_dec_reply,
+ &error_string);
+ if (code != 0) {
++ ap_rep_blob = data_blob_null;
+ error_code = code;
+ goto reply;
+ }
+@@ -262,6 +263,7 @@ kdc_code kpasswd_process(struct kdc_server *kdc,
+ &kpasswd_dec_reply,
+ &enc_data_blob);
+ if (!NT_STATUS_IS_OK(status)) {
++ ap_rep_blob = data_blob_null;
+ error_code = KRB5_KPASSWD_HARDERROR;
+ error_string = talloc_asprintf(tmp_ctx,
+ "gensec_wrap failed - %s\n",
+--
+2.25.1
+
+
+From cf9e37604409ba0c3c5904af40beb2975c309ad4 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Fri, 27 May 2022 19:29:34 +1200
+Subject: [PATCH 74/99] CVE-2022-2031 lib:krb5_wrap: Generate valid error codes
+ in smb_krb5_mk_error()
+
+The error code passed in will be an offset from ERROR_TABLE_BASE_krb5,
+so we need to subtract that before creating the error. Heimdal does this
+internally, so it isn't needed there.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ lib/krb5_wrap/krb5_samba.c | 2 +-
+ selftest/knownfail_mit_kdc | 4 ++++
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git lib/krb5_wrap/krb5_samba.c lib/krb5_wrap/krb5_samba.c
+index 76c2dcd2126..610efcc9b87 100644
+--- lib/krb5_wrap/krb5_samba.c
++++ lib/krb5_wrap/krb5_samba.c
+@@ -237,7 +237,7 @@ krb5_error_code smb_krb5_mk_error(krb5_context context,
+ return code;
+ }
+
+- errpkt.error = error_code;
++ errpkt.error = error_code - ERROR_TABLE_BASE_krb5;
+
+ errpkt.text.length = 0;
+ if (e_text != NULL) {
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index f64291e776d..633bf79e8e0 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -579,9 +579,13 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ # Kpasswd tests
+ #
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_empty.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_seq_number.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_non_initial.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+--
+2.25.1
+
+
+From cf749fac346ef59c91a9ea87f5e7ddec2e5649c7 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 18 May 2022 16:49:43 +1200
+Subject: [PATCH 75/99] CVE-2022-2031 s4:kpasswd: Return a kpasswd error code
+ in KRB-ERROR
+
+If we attempt to return an error code outside of Heimdal's allowed range
+[KRB5KDC_ERR_NONE, KRB5_ERR_RCSID), it will be replaced with a GENERIC
+error, and the error text will be set to the meaningless result of
+krb5_get_error_message(). Avoid this by ensuring the error code is in
+the correct range.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ selftest/knownfail_heimdal_kdc | 2 --
+ selftest/knownfail_mit_kdc | 4 ----
+ source4/kdc/kpasswd-service.c | 2 +-
+ 3 files changed, 1 insertion(+), 7 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 40e24f3155b..3b494baa658 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -276,9 +276,7 @@
+ #
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_empty.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_seq_number.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_non_initial.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 633bf79e8e0..f64291e776d 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -579,13 +579,9 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ # Kpasswd tests
+ #
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_empty.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_seq_number.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_non_initial.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+diff --git source4/kdc/kpasswd-service.c source4/kdc/kpasswd-service.c
+index a3c57a67dd1..b4706de1ad7 100644
+--- source4/kdc/kpasswd-service.c
++++ source4/kdc/kpasswd-service.c
+@@ -312,7 +312,7 @@ reply:
+ }
+
+ code = smb_krb5_mk_error(kdc->smb_krb5_context->krb5_context,
+- error_code,
++ KRB5KDC_ERR_NONE + error_code,
+ NULL, /* e_text */
+ &k_dec_data,
+ NULL, /* client */
+--
+2.25.1
+
+
+From 198256e2184897300e1cea4343437c3b7b6f74ad Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 18 May 2022 16:06:31 +1200
+Subject: [PATCH 76/99] CVE-2022-2031 gensec_krb5: Add helper function to check
+ if client sent an initial ticket
+
+This will be used in the kpasswd service to ensure that the client has
+an initial ticket to kadmin/changepw, and not a service ticket.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ source4/auth/gensec/gensec_krb5.c | 20 +-----
+ source4/auth/gensec/gensec_krb5_helpers.c | 72 ++++++++++++++++++++++
+ source4/auth/gensec/gensec_krb5_helpers.h | 32 ++++++++++
+ source4/auth/gensec/gensec_krb5_internal.h | 47 ++++++++++++++
+ source4/auth/gensec/wscript_build | 4 ++
+ 5 files changed, 157 insertions(+), 18 deletions(-)
+ create mode 100644 source4/auth/gensec/gensec_krb5_helpers.c
+ create mode 100644 source4/auth/gensec/gensec_krb5_helpers.h
+ create mode 100644 source4/auth/gensec/gensec_krb5_internal.h
+
+diff --git source4/auth/gensec/gensec_krb5.c source4/auth/gensec/gensec_krb5.c
+index 7d87b3ac6b9..104e4639c44 100644
+--- source4/auth/gensec/gensec_krb5.c
++++ source4/auth/gensec/gensec_krb5.c
+@@ -44,27 +44,11 @@
+ #include "../lib/util/asn1.h"
+ #include "auth/kerberos/pac_utils.h"
+ #include "gensec_krb5.h"
++#include "gensec_krb5_internal.h"
++#include "gensec_krb5_helpers.h"
+
+ _PUBLIC_ NTSTATUS gensec_krb5_init(TALLOC_CTX *);
+
+-enum GENSEC_KRB5_STATE {
+- GENSEC_KRB5_SERVER_START,
+- GENSEC_KRB5_CLIENT_START,
+- GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
+- GENSEC_KRB5_DONE
+-};
+-
+-struct gensec_krb5_state {
+- enum GENSEC_KRB5_STATE state_position;
+- struct smb_krb5_context *smb_krb5_context;
+- krb5_auth_context auth_context;
+- krb5_data enc_ticket;
+- krb5_keyblock *keyblock;
+- krb5_ticket *ticket;
+- bool gssapi;
+- krb5_flags ap_req_options;
+-};
+-
+ static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state)
+ {
+ if (!gensec_krb5_state->smb_krb5_context) {
+diff --git source4/auth/gensec/gensec_krb5_helpers.c source4/auth/gensec/gensec_krb5_helpers.c
+new file mode 100644
+index 00000000000..21f2f1e884e
+--- /dev/null
++++ source4/auth/gensec/gensec_krb5_helpers.c
+@@ -0,0 +1,72 @@
++/*
++ Unix SMB/CIFS implementation.
++
++ Kerberos backend for GENSEC
++
++ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
++ Copyright (C) Andrew Tridgell 2001
++ Copyright (C) Luke Howard 2002-2003
++ Copyright (C) Stefan Metzmacher 2004-2005
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include "includes.h"
++#include "auth/auth.h"
++#include "auth/gensec/gensec.h"
++#include "auth/gensec/gensec_internal.h"
++#include "gensec_krb5_internal.h"
++#include "gensec_krb5_helpers.h"
++#include "system/kerberos.h"
++#include "auth/kerberos/kerberos.h"
++
++static struct gensec_krb5_state *get_private_state(const struct gensec_security *gensec_security)
++{
++ struct gensec_krb5_state *gensec_krb5_state = NULL;
++
++ if (strcmp(gensec_security->ops->name, "krb5") != 0) {
++ /* We require that the krb5 mechanism is being used. */
++ return NULL;
++ }
++
++ gensec_krb5_state = talloc_get_type(gensec_security->private_data,
++ struct gensec_krb5_state);
++ return gensec_krb5_state;
++}
++
++/*
++ * Returns 1 if our ticket has the initial flag set, 0 if not, and -1 in case of
++ * error.
++ */
++int gensec_krb5_initial_ticket(const struct gensec_security *gensec_security)
++{
++ struct gensec_krb5_state *gensec_krb5_state = NULL;
++
++ gensec_krb5_state = get_private_state(gensec_security);
++ if (gensec_krb5_state == NULL) {
++ return -1;
++ }
++
++ if (gensec_krb5_state->ticket == NULL) {
++ /* We don't have a ticket */
++ return -1;
++ }
++
++#ifdef SAMBA4_USES_HEIMDAL
++ return gensec_krb5_state->ticket->ticket.flags.initial;
++#else /* MIT KERBEROS */
++ return (gensec_krb5_state->ticket->enc_part2->flags & TKT_FLG_INITIAL) ? 1 : 0;
++#endif /* SAMBA4_USES_HEIMDAL */
++}
+diff --git source4/auth/gensec/gensec_krb5_helpers.h source4/auth/gensec/gensec_krb5_helpers.h
+new file mode 100644
+index 00000000000..d7b694dad0c
+--- /dev/null
++++ source4/auth/gensec/gensec_krb5_helpers.h
+@@ -0,0 +1,32 @@
++/*
++ Unix SMB/CIFS implementation.
++
++ Kerberos backend for GENSEC
++
++ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
++ Copyright (C) Andrew Tridgell 2001
++ Copyright (C) Luke Howard 2002-2003
++ Copyright (C) Stefan Metzmacher 2004-2005
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>.
++*/
++
++struct gensec_security;
++
++/*
++ * Returns 1 if our ticket has the initial flag set, 0 if not, and -1 in case of
++ * error.
++ */
++int gensec_krb5_initial_ticket(const struct gensec_security *gensec_security);
+diff --git source4/auth/gensec/gensec_krb5_internal.h source4/auth/gensec/gensec_krb5_internal.h
+new file mode 100644
+index 00000000000..0bb796f1b2a
+--- /dev/null
++++ source4/auth/gensec/gensec_krb5_internal.h
+@@ -0,0 +1,47 @@
++/*
++ Unix SMB/CIFS implementation.
++
++ Kerberos backend for GENSEC
++
++ Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004
++ Copyright (C) Andrew Tridgell 2001
++ Copyright (C) Luke Howard 2002-2003
++ Copyright (C) Stefan Metzmacher 2004-2005
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 3 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++
++ You should have received a copy of the GNU General Public License
++ along with this program. If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include "includes.h"
++#include "auth/gensec/gensec.h"
++#include "system/kerberos.h"
++#include "auth/kerberos/kerberos.h"
++
++enum GENSEC_KRB5_STATE {
++ GENSEC_KRB5_SERVER_START,
++ GENSEC_KRB5_CLIENT_START,
++ GENSEC_KRB5_CLIENT_MUTUAL_AUTH,
++ GENSEC_KRB5_DONE
++};
++
++struct gensec_krb5_state {
++ enum GENSEC_KRB5_STATE state_position;
++ struct smb_krb5_context *smb_krb5_context;
++ krb5_auth_context auth_context;
++ krb5_data enc_ticket;
++ krb5_keyblock *keyblock;
++ krb5_ticket *ticket;
++ bool gssapi;
++ krb5_flags ap_req_options;
++};
+diff --git source4/auth/gensec/wscript_build source4/auth/gensec/wscript_build
+index d14a50ff273..20271f1665b 100644
+--- source4/auth/gensec/wscript_build
++++ source4/auth/gensec/wscript_build
+@@ -18,6 +18,10 @@ bld.SAMBA_MODULE('gensec_krb5',
+ enabled=bld.AD_DC_BUILD_IS_ENABLED()
+ )
+
++bld.SAMBA_SUBSYSTEM('gensec_krb5_helpers',
++ source='gensec_krb5_helpers.c',
++ deps='gensec_krb5',
++ enabled=bld.AD_DC_BUILD_IS_ENABLED())
+
+ bld.SAMBA_MODULE('gensec_gssapi',
+ source='gensec_gssapi.c',
+--
+2.25.1
+
+
+From 6c4fd575d706b2695090941ad7947b30abdb9071 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 18 May 2022 16:52:41 +1200
+Subject: [PATCH 77/99] CVE-2022-2031 s4:kpasswd: Require an initial ticket
+
+Ensure that for password changes the client uses an AS-REQ to get the
+ticket to kpasswd, and not a TGS-REQ.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Removed MIT KDC 1.20-specific knownfails]
+---
+ selftest/knownfail_heimdal_kdc | 1 -
+ selftest/knownfail_mit_kdc | 1 -
+ source4/kdc/kpasswd-service-heimdal.c | 17 +++++++++++++++++
+ source4/kdc/kpasswd-service-mit.c | 17 +++++++++++++++++
+ source4/kdc/wscript_build | 1 +
+ 5 files changed, 35 insertions(+), 2 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 3b494baa658..5cd8615f6a9 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -277,7 +277,6 @@
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_non_initial.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index f64291e776d..46b0f1fa9ed 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -580,7 +580,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ #
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_non_initial.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+diff --git source4/kdc/kpasswd-service-heimdal.c source4/kdc/kpasswd-service-heimdal.c
+index c804852c3a7..1a6c2b60d03 100644
+--- source4/kdc/kpasswd-service-heimdal.c
++++ source4/kdc/kpasswd-service-heimdal.c
+@@ -24,6 +24,7 @@
+ #include "param/param.h"
+ #include "auth/auth.h"
+ #include "auth/gensec/gensec.h"
++#include "gensec_krb5_helpers.h"
+ #include "kdc/kdc-server.h"
+ #include "kdc/kpasswd_glue.h"
+ #include "kdc/kpasswd-service.h"
+@@ -31,6 +32,7 @@
+
+ static krb5_error_code kpasswd_change_password(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
++ const struct gensec_security *gensec_security,
+ struct auth_session_info *session_info,
+ DATA_BLOB *password,
+ DATA_BLOB *kpasswd_reply,
+@@ -42,6 +44,17 @@ static krb5_error_code kpasswd_change_password(struct kdc_server *kdc,
+ const char *reject_string = NULL;
+ struct samr_DomInfo1 *dominfo;
+ bool ok;
++ int ret;
++
++ /*
++ * We're doing a password change (rather than a password set), so check
++ * that we were given an initial ticket.
++ */
++ ret = gensec_krb5_initial_ticket(gensec_security);
++ if (ret != 1) {
++ *error_string = "Expected an initial ticket";
++ return KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
++ }
+
+ status = samdb_kpasswd_change_password(mem_ctx,
+ kdc->task->lp_ctx,
+@@ -81,6 +94,7 @@ static krb5_error_code kpasswd_change_password(struct kdc_server *kdc,
+
+ static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
++ const struct gensec_security *gensec_security,
+ struct auth_session_info *session_info,
+ DATA_BLOB *decoded_data,
+ DATA_BLOB *kpasswd_reply,
+@@ -173,6 +187,7 @@ static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
+ free_ChangePasswdDataMS(&chpw);
+ return kpasswd_change_password(kdc,
+ mem_ctx,
++ gensec_security,
+ session_info,
+ &password,
+ kpasswd_reply,
+@@ -272,6 +287,7 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
+
+ return kpasswd_change_password(kdc,
+ mem_ctx,
++ gensec_security,
+ session_info,
+ &password,
+ kpasswd_reply,
+@@ -280,6 +296,7 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
+ case KRB5_KPASSWD_VERS_SETPW: {
+ return kpasswd_set_password(kdc,
+ mem_ctx,
++ gensec_security,
+ session_info,
+ decoded_data,
+ kpasswd_reply,
+diff --git source4/kdc/kpasswd-service-mit.c source4/kdc/kpasswd-service-mit.c
+index 9c4d2801669..de4c6f3f622 100644
+--- source4/kdc/kpasswd-service-mit.c
++++ source4/kdc/kpasswd-service-mit.c
+@@ -24,6 +24,7 @@
+ #include "param/param.h"
+ #include "auth/auth.h"
+ #include "auth/gensec/gensec.h"
++#include "gensec_krb5_helpers.h"
+ #include "kdc/kdc-server.h"
+ #include "kdc/kpasswd_glue.h"
+ #include "kdc/kpasswd-service.h"
+@@ -84,6 +85,7 @@ out:
+
+ static krb5_error_code kpasswd_change_password(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
++ const struct gensec_security *gensec_security,
+ struct auth_session_info *session_info,
+ DATA_BLOB *password,
+ DATA_BLOB *kpasswd_reply,
+@@ -95,6 +97,17 @@ static krb5_error_code kpasswd_change_password(struct kdc_server *kdc,
+ const char *reject_string = NULL;
+ struct samr_DomInfo1 *dominfo;
+ bool ok;
++ int ret;
++
++ /*
++ * We're doing a password change (rather than a password set), so check
++ * that we were given an initial ticket.
++ */
++ ret = gensec_krb5_initial_ticket(gensec_security);
++ if (ret != 1) {
++ *error_string = "Expected an initial ticket";
++ return KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
++ }
+
+ status = samdb_kpasswd_change_password(mem_ctx,
+ kdc->task->lp_ctx,
+@@ -134,6 +147,7 @@ static krb5_error_code kpasswd_change_password(struct kdc_server *kdc,
+
+ static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
+ TALLOC_CTX *mem_ctx,
++ const struct gensec_security *gensec_security,
+ struct auth_session_info *session_info,
+ DATA_BLOB *decoded_data,
+ DATA_BLOB *kpasswd_reply,
+@@ -250,6 +264,7 @@ static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
+
+ return kpasswd_change_password(kdc,
+ mem_ctx,
++ gensec_security,
+ session_info,
+ &password,
+ kpasswd_reply,
+@@ -350,6 +365,7 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
+
+ return kpasswd_change_password(kdc,
+ mem_ctx,
++ gensec_security,
+ session_info,
+ &password,
+ kpasswd_reply,
+@@ -358,6 +374,7 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
+ case RFC3244_VERSION: {
+ return kpasswd_set_password(kdc,
+ mem_ctx,
++ gensec_security,
+ session_info,
+ decoded_data,
+ kpasswd_reply,
+diff --git source4/kdc/wscript_build source4/kdc/wscript_build
+index 0edca94e75f..13ba3947cf6 100644
+--- source4/kdc/wscript_build
++++ source4/kdc/wscript_build
+@@ -88,6 +88,7 @@ bld.SAMBA_SUBSYSTEM('KPASSWD-SERVICE',
+ krb5samba
+ samba_server_gensec
+ KPASSWD_GLUE
++ gensec_krb5_helpers
+ ''')
+
+ bld.SAMBA_SUBSYSTEM('KDC-GLUE',
+--
+2.25.1
+
+
+From 69e742e6208bd471eb509795bd753a0c98392bf6 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 18 May 2022 17:11:49 +1200
+Subject: [PATCH 78/99] s4:kpasswd: Restructure code for clarity
+
+View with 'git show -b'.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ source4/kdc/kpasswd-service-heimdal.c | 46 +++++++++++++--------------
+ 1 file changed, 22 insertions(+), 24 deletions(-)
+
+diff --git source4/kdc/kpasswd-service-heimdal.c source4/kdc/kpasswd-service-heimdal.c
+index 1a6c2b60d03..a0352d1ad35 100644
+--- source4/kdc/kpasswd-service-heimdal.c
++++ source4/kdc/kpasswd-service-heimdal.c
+@@ -160,30 +160,7 @@ static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
+ return 0;
+ }
+
+- if (chpw.targname != NULL && chpw.targrealm != NULL) {
+- code = krb5_build_principal_ext(context,
+- &target_principal,
+- strlen(*chpw.targrealm),
+- *chpw.targrealm,
+- 0);
+- if (code != 0) {
+- free_ChangePasswdDataMS(&chpw);
+- return kpasswd_make_error_reply(mem_ctx,
+- KRB5_KPASSWD_MALFORMED,
+- "Failed to parse principal",
+- kpasswd_reply);
+- }
+- code = copy_PrincipalName(chpw.targname,
+- &target_principal->name);
+- if (code != 0) {
+- free_ChangePasswdDataMS(&chpw);
+- krb5_free_principal(context, target_principal);
+- return kpasswd_make_error_reply(mem_ctx,
+- KRB5_KPASSWD_MALFORMED,
+- "Failed to parse principal",
+- kpasswd_reply);
+- }
+- } else {
++ if (chpw.targname == NULL || chpw.targrealm == NULL) {
+ free_ChangePasswdDataMS(&chpw);
+ return kpasswd_change_password(kdc,
+ mem_ctx,
+@@ -193,7 +170,28 @@ static krb5_error_code kpasswd_set_password(struct kdc_server *kdc,
+ kpasswd_reply,
+ error_string);
+ }
++ code = krb5_build_principal_ext(context,
++ &target_principal,
++ strlen(*chpw.targrealm),
++ *chpw.targrealm,
++ 0);
++ if (code != 0) {
++ free_ChangePasswdDataMS(&chpw);
++ return kpasswd_make_error_reply(mem_ctx,
++ KRB5_KPASSWD_MALFORMED,
++ "Failed to parse principal",
++ kpasswd_reply);
++ }
++ code = copy_PrincipalName(chpw.targname,
++ &target_principal->name);
+ free_ChangePasswdDataMS(&chpw);
++ if (code != 0) {
++ krb5_free_principal(context, target_principal);
++ return kpasswd_make_error_reply(mem_ctx,
++ KRB5_KPASSWD_MALFORMED,
++ "Failed to parse principal",
++ kpasswd_reply);
++ }
+
+ if (target_principal->name.name_string.len >= 2) {
+ is_service_principal = true;
+--
+2.25.1
+
+
+From b5adf7cc6d740c8f4f7b5888f106de24a1181da7 Mon Sep 17 00:00:00 2001
+From: Andreas Schneider <asn@samba.org>
+Date: Tue, 24 May 2022 10:17:00 +0200
+Subject: [PATCH 79/99] CVE-2022-2031 testprogs: Fix auth with smbclient and
+ krb5 ccache
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Andreas Schneider <asn@samba.org>
+Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+
+[jsutton@samba.org Fixed conflict and renamed --use-krb5-ccache to
+ --krb5-ccache]
+---
+ testprogs/blackbox/test_kpasswd_heimdal.sh | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git testprogs/blackbox/test_kpasswd_heimdal.sh testprogs/blackbox/test_kpasswd_heimdal.sh
+index 7351ce022d1..1e895daa162 100755
+--- testprogs/blackbox/test_kpasswd_heimdal.sh
++++ testprogs/blackbox/test_kpasswd_heimdal.sh
+@@ -72,7 +72,7 @@ testit "kinit with user password" \
+ do_kinit $TEST_PRINCIPAL $TEST_PASSWORD || failed=`expr $failed + 1`
+
+ test_smbclient "Test login with user kerberos ccache" \
+- "ls" "$SMB_UNC" -k yes || failed=`expr $failed + 1`
++ "ls" "$SMB_UNC" --krb5-ccache=${KRB5CCNAME} || failed=`expr $failed + 1`
+
+ testit "change user password with 'samba-tool user password' (unforced)" \
+ $VALGRIND $PYTHON $samba_tool user password -W$DOMAIN -U$TEST_USERNAME%$TEST_PASSWORD -k no --newpassword=$TEST_PASSWORD_NEW || failed=`expr $failed + 1`
+@@ -85,7 +85,7 @@ testit "kinit with user password" \
+ do_kinit $TEST_PRINCIPAL $TEST_PASSWORD || failed=`expr $failed + 1`
+
+ test_smbclient "Test login with user kerberos ccache" \
+- "ls" "$SMB_UNC" -k yes || failed=`expr $failed + 1`
++ "ls" "$SMB_UNC" --krb5-ccache=${KRB5CCNAME} || failed=`expr $failed + 1`
+
+ ###########################################################
+ ### check that a short password is rejected
+--
+2.25.1
+
+
+From 91a1b0955a053f73e6d531f0f12eaa604aca79d7 Mon Sep 17 00:00:00 2001
+From: Andreas Schneider <asn@samba.org>
+Date: Thu, 19 May 2022 16:35:28 +0200
+Subject: [PATCH 80/99] CVE-2022-2031 testprogs: Add kadmin/changepw
+ canonicalization test with MIT kpasswd
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Andreas Schneider <asn@samba.org>
+Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+---
+ selftest/knownfail.d/kadmin_changepw | 1 +
+ testprogs/blackbox/test_kpasswd_heimdal.sh | 35 +++++++++++++++++++++-
+ 2 files changed, 35 insertions(+), 1 deletion(-)
+ create mode 100644 selftest/knownfail.d/kadmin_changepw
+
+diff --git selftest/knownfail.d/kadmin_changepw selftest/knownfail.d/kadmin_changepw
+new file mode 100644
+index 00000000000..97c14793ea5
+--- /dev/null
++++ selftest/knownfail.d/kadmin_changepw
+@@ -0,0 +1 @@
++^samba4.blackbox.kpasswd.MIT kpasswd.change.user.password
+diff --git testprogs/blackbox/test_kpasswd_heimdal.sh testprogs/blackbox/test_kpasswd_heimdal.sh
+index 1e895daa162..059b7a8e4d1 100755
+--- testprogs/blackbox/test_kpasswd_heimdal.sh
++++ testprogs/blackbox/test_kpasswd_heimdal.sh
+@@ -7,7 +7,7 @@
+
+ if [ $# -lt 6 ]; then
+ cat <<EOF
+-Usage: test_passwords.sh SERVER USERNAME PASSWORD REALM DOMAIN PREFIX SMBCLIENT
++Usage: test_kpasswd_heimdal.sh SERVER USERNAME PASSWORD REALM DOMAIN PREFIX SMBCLIENT
+ EOF
+ exit 1;
+ fi
+@@ -27,6 +27,8 @@ smbclient="$samba_bindir/smbclient"
+ samba_kinit=$samba_bindir/samba4kinit
+ samba_kpasswd=$samba_bindir/samba4kpasswd
+
++mit_kpasswd="$(command -v kpasswd)"
++
+ samba_tool="$samba_bindir/samba-tool"
+ net_tool="$samba_bindir/net"
+ texpect="$samba_bindir/texpect"
+@@ -142,6 +144,37 @@ testit "kpasswd change user password" \
+ TEST_PASSWORD=$TEST_PASSWORD_NEW
+ TEST_PASSWORD_NEW="testPaSS@03%"
+
++###########################################################
++### CVE-2022-XXXXX
++###########################################################
++
++if [ -n "${mit_kpasswd}" ]; then
++ cat > "${PREFIX}/tmpkpasswdscript" <<EOF
++expect Password for ${TEST_PRINCIPAL}
++password ${TEST_PASSWORD}\n
++expect Enter new password
++send ${TEST_PASSWORD_NEW}\n
++expect Enter it again
++send ${TEST_PASSWORD_NEW}\n
++expect Password changed.
++EOF
++
++ SAVE_KRB5_CONFIG="${KRB5_CONFIG}"
++ KRB5_CONFIG="${PREFIX}/tmpkrb5.conf"
++ export KRB5_CONFIG
++ sed -e 's/\[libdefaults\]/[libdefaults]\n canonicalize = yes/' \
++ "${SAVE_KRB5_CONFIG}" > "${KRB5_CONFIG}"
++ testit "MIT kpasswd change user password" \
++ "${texpect}" "${PREFIX}/tmpkpasswdscript" "${mit_kpasswd}" \
++ "${TEST_PRINCIPAL}" ||
++ failed=$((failed + 1))
++ KRB5_CONFIG="${SAVE_KRB5_CONFIG}"
++ export KRB5_CONFIG
++fi
++
++TEST_PASSWORD="${TEST_PASSWORD_NEW}"
++TEST_PASSWORD_NEW="testPaSS@03force%"
++
+ ###########################################################
+ ### Force password change at login
+ ###########################################################
+--
+2.25.1
+
+
+From 36d94ffb9c99f3e515024424020e3e03e98f34f5 Mon Sep 17 00:00:00 2001
+From: Andreas Schneider <asn@samba.org>
+Date: Tue, 24 May 2022 09:54:18 +0200
+Subject: [PATCH 81/99] CVE-2022-2031 s4:kdc: Implement is_kadmin_changepw()
+ helper function
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Andreas Schneider <asn@samba.org>
+Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+
+[jsutton@samba.org Adapted entry to entry_ex->entry]
+---
+ source4/kdc/db-glue.c | 16 +++++++++++-----
+ 1 file changed, 11 insertions(+), 5 deletions(-)
+
+diff --git source4/kdc/db-glue.c source4/kdc/db-glue.c
+index 5752ffb821c..45159e6e64d 100644
+--- source4/kdc/db-glue.c
++++ source4/kdc/db-glue.c
+@@ -816,6 +816,14 @@ static int principal_comp_strcmp(krb5_context context,
+ component, string, false);
+ }
+
++static bool is_kadmin_changepw(krb5_context context,
++ krb5_const_principal principal)
++{
++ return krb5_princ_size(context, principal) == 2 &&
++ (principal_comp_strcmp(context, principal, 0, "kadmin") == 0) &&
++ (principal_comp_strcmp(context, principal, 1, "changepw") == 0);
++}
++
+ /*
+ * Construct an hdb_entry from a directory entry.
+ */
+@@ -1110,11 +1118,9 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
+ * 'change password', as otherwise we could get into
+ * trouble, and not enforce the password expirty.
+ * Instead, only do it when request is for the kpasswd service */
+- if (ent_type == SAMBA_KDC_ENT_TYPE_SERVER
+- && krb5_princ_size(context, principal) == 2
+- && (principal_comp_strcmp(context, principal, 0, "kadmin") == 0)
+- && (principal_comp_strcmp(context, principal, 1, "changepw") == 0)
+- && lpcfg_is_my_domain_or_realm(lp_ctx, realm)) {
++ if (ent_type == SAMBA_KDC_ENT_TYPE_SERVER &&
++ is_kadmin_changepw(context, principal) &&
++ lpcfg_is_my_domain_or_realm(lp_ctx, realm)) {
+ entry_ex->entry.flags.change_pw = 1;
+ }
+
+--
+2.25.1
+
+
+From f68877af829bf73da8e965c9458a9846d1757038 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 18 May 2022 16:56:01 +1200
+Subject: [PATCH 82/99] CVE-2022-2031 s4:kdc: Split out a
+ samba_kdc_get_entry_principal() function
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Adapted entry to entry_ex->entry]
+
+[jsutton@samba.org Fixed conflicts caused by superfluous whitespace]
+---
+ source4/kdc/db-glue.c | 192 +++++++++++++++++++++++-------------------
+ 1 file changed, 107 insertions(+), 85 deletions(-)
+
+diff --git source4/kdc/db-glue.c source4/kdc/db-glue.c
+index 45159e6e64d..ac0c206b5c1 100644
+--- source4/kdc/db-glue.c
++++ source4/kdc/db-glue.c
+@@ -824,6 +824,101 @@ static bool is_kadmin_changepw(krb5_context context,
+ (principal_comp_strcmp(context, principal, 1, "changepw") == 0);
+ }
+
++static krb5_error_code samba_kdc_get_entry_principal(
++ krb5_context context,
++ struct samba_kdc_db_context *kdc_db_ctx,
++ const char *samAccountName,
++ enum samba_kdc_ent_type ent_type,
++ unsigned flags,
++ krb5_const_principal in_princ,
++ krb5_principal *out_princ)
++{
++ struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
++ krb5_error_code ret = 0;
++
++ /*
++ * If we are set to canonicalize, we get back the fixed UPPER
++ * case realm, and the real username (ie matching LDAP
++ * samAccountName)
++ *
++ * Otherwise, if we are set to enterprise, we
++ * get back the whole principal as-sent
++ *
++ * Finally, if we are not set to canonicalize, we get back the
++ * fixed UPPER case realm, but the as-sent username
++ */
++
++ if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT) {
++ if (flags & (SDB_F_CANON|SDB_F_FORCE_CANON)) {
++ /*
++ * When requested to do so, ensure that the
++ * both realm values in the principal are set
++ * to the upper case, canonical realm
++ */
++ ret = smb_krb5_make_principal(context, out_princ,
++ lpcfg_realm(lp_ctx), "krbtgt",
++ lpcfg_realm(lp_ctx), NULL);
++ if (ret) {
++ return ret;
++ }
++ smb_krb5_principal_set_type(context, *out_princ, KRB5_NT_SRV_INST);
++ } else {
++ ret = krb5_copy_principal(context, in_princ, out_princ);
++ if (ret) {
++ return ret;
++ }
++ /*
++ * this appears to be required regardless of
++ * the canonicalize flag from the client
++ */
++ ret = smb_krb5_principal_set_realm(context, *out_princ, lpcfg_realm(lp_ctx));
++ if (ret) {
++ return ret;
++ }
++ }
++
++ } else if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && in_princ == NULL) {
++ ret = smb_krb5_make_principal(context, out_princ, lpcfg_realm(lp_ctx), samAccountName, NULL);
++ if (ret) {
++ return ret;
++ }
++ } else if ((flags & SDB_F_FORCE_CANON) ||
++ ((flags & SDB_F_CANON) && (flags & SDB_F_FOR_AS_REQ))) {
++ /*
++ * SDB_F_CANON maps from the canonicalize flag in the
++ * packet, and has a different meaning between AS-REQ
++ * and TGS-REQ. We only change the principal in the AS-REQ case
++ *
++ * The SDB_F_FORCE_CANON if for new MIT KDC code that wants
++ * the canonical name in all lookups, and takes care to
++ * canonicalize only when appropriate.
++ */
++ ret = smb_krb5_make_principal(context, out_princ, lpcfg_realm(lp_ctx), samAccountName, NULL);
++ if (ret) {
++ return ret;
++ }
++ } else {
++ ret = krb5_copy_principal(context, in_princ, out_princ);
++ if (ret) {
++ return ret;
++ }
++
++ /* While we have copied the client principal, tests
++ * show that Win2k3 returns the 'corrected' realm, not
++ * the client-specified realm. This code attempts to
++ * replace the client principal's realm with the one
++ * we determine from our records */
++
++ /* this has to be with malloc() */
++ ret = smb_krb5_principal_set_realm(context, *out_princ, lpcfg_realm(lp_ctx));
++ if (ret) {
++ return ret;
++ }
++ }
++
++ return 0;
++}
++
+ /*
+ * Construct an hdb_entry from a directory entry.
+ */
+@@ -913,93 +1008,8 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
+ userAccountControl |= msDS_User_Account_Control_Computed;
+ }
+
+- /*
+- * If we are set to canonicalize, we get back the fixed UPPER
+- * case realm, and the real username (ie matching LDAP
+- * samAccountName)
+- *
+- * Otherwise, if we are set to enterprise, we
+- * get back the whole principal as-sent
+- *
+- * Finally, if we are not set to canonicalize, we get back the
+- * fixed UPPER case realm, but the as-sent username
+- */
+-
+ if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT) {
+ p->is_krbtgt = true;
+-
+- if (flags & (SDB_F_CANON|SDB_F_FORCE_CANON)) {
+- /*
+- * When requested to do so, ensure that the
+- * both realm values in the principal are set
+- * to the upper case, canonical realm
+- */
+- ret = smb_krb5_make_principal(context, &entry_ex->entry.principal,
+- lpcfg_realm(lp_ctx), "krbtgt",
+- lpcfg_realm(lp_ctx), NULL);
+- if (ret) {
+- krb5_clear_error_message(context);
+- goto out;
+- }
+- smb_krb5_principal_set_type(context, entry_ex->entry.principal, KRB5_NT_SRV_INST);
+- } else {
+- ret = krb5_copy_principal(context, principal, &entry_ex->entry.principal);
+- if (ret) {
+- krb5_clear_error_message(context);
+- goto out;
+- }
+- /*
+- * this appears to be required regardless of
+- * the canonicalize flag from the client
+- */
+- ret = smb_krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx));
+- if (ret) {
+- krb5_clear_error_message(context);
+- goto out;
+- }
+- }
+-
+- } else if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && principal == NULL) {
+- ret = smb_krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL);
+- if (ret) {
+- krb5_clear_error_message(context);
+- goto out;
+- }
+- } else if ((flags & SDB_F_FORCE_CANON) ||
+- ((flags & SDB_F_CANON) && (flags & SDB_F_FOR_AS_REQ))) {
+- /*
+- * SDB_F_CANON maps from the canonicalize flag in the
+- * packet, and has a different meaning between AS-REQ
+- * and TGS-REQ. We only change the principal in the AS-REQ case
+- *
+- * The SDB_F_FORCE_CANON if for new MIT KDC code that wants
+- * the canonical name in all lookups, and takes care to
+- * canonicalize only when appropriate.
+- */
+- ret = smb_krb5_make_principal(context, &entry_ex->entry.principal, lpcfg_realm(lp_ctx), samAccountName, NULL);
+- if (ret) {
+- krb5_clear_error_message(context);
+- goto out;
+- }
+- } else {
+- ret = krb5_copy_principal(context, principal, &entry_ex->entry.principal);
+- if (ret) {
+- krb5_clear_error_message(context);
+- goto out;
+- }
+-
+- /* While we have copied the client principal, tests
+- * show that Win2k3 returns the 'corrected' realm, not
+- * the client-specified realm. This code attempts to
+- * replace the client principal's realm with the one
+- * we determine from our records */
+-
+- /* this has to be with malloc() */
+- ret = smb_krb5_principal_set_realm(context, entry_ex->entry.principal, lpcfg_realm(lp_ctx));
+- if (ret) {
+- krb5_clear_error_message(context);
+- goto out;
+- }
+ }
+
+ /* First try and figure out the flags based on the userAccountControl */
+@@ -1185,6 +1195,18 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
+ }
+ }
+
++ ret = samba_kdc_get_entry_principal(context,
++ kdc_db_ctx,
++ samAccountName,
++ ent_type,
++ flags,
++ principal,
++ &entry_ex->entry.principal);
++ if (ret != 0) {
++ krb5_clear_error_message(context);
++ goto out;
++ }
++
+ entry_ex->entry.valid_start = NULL;
+
+ entry_ex->entry.max_life = malloc(sizeof(*entry_ex->entry.max_life));
+--
+2.25.1
+
+
+From fa4742e1b9dea0b9c379f00666478bd41c021634 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 25 May 2022 17:19:58 +1200
+Subject: [PATCH 83/99] CVE-2022-2031 s4:kdc: Refactor
+ samba_kdc_get_entry_principal()
+
+This eliminates some duplicate branches.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Pair-Programmed-With: Andreas Schneider <asn@samba.org>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ source4/kdc/db-glue.c | 116 ++++++++++++++++++++----------------------
+ 1 file changed, 55 insertions(+), 61 deletions(-)
+
+diff --git source4/kdc/db-glue.c source4/kdc/db-glue.c
+index ac0c206b5c1..385c118a073 100644
+--- source4/kdc/db-glue.c
++++ source4/kdc/db-glue.c
+@@ -834,7 +834,8 @@ static krb5_error_code samba_kdc_get_entry_principal(
+ krb5_principal *out_princ)
+ {
+ struct loadparm_context *lp_ctx = kdc_db_ctx->lp_ctx;
+- krb5_error_code ret = 0;
++ krb5_error_code code = 0;
++ bool canon = flags & (SDB_F_CANON|SDB_F_FORCE_CANON);
+
+ /*
+ * If we are set to canonicalize, we get back the fixed UPPER
+@@ -848,75 +849,68 @@ static krb5_error_code samba_kdc_get_entry_principal(
+ * fixed UPPER case realm, but the as-sent username
+ */
+
+- if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT) {
+- if (flags & (SDB_F_CANON|SDB_F_FORCE_CANON)) {
+- /*
+- * When requested to do so, ensure that the
+- * both realm values in the principal are set
+- * to the upper case, canonical realm
+- */
+- ret = smb_krb5_make_principal(context, out_princ,
+- lpcfg_realm(lp_ctx), "krbtgt",
+- lpcfg_realm(lp_ctx), NULL);
+- if (ret) {
+- return ret;
+- }
+- smb_krb5_principal_set_type(context, *out_princ, KRB5_NT_SRV_INST);
+- } else {
+- ret = krb5_copy_principal(context, in_princ, out_princ);
+- if (ret) {
+- return ret;
+- }
+- /*
+- * this appears to be required regardless of
+- * the canonicalize flag from the client
+- */
+- ret = smb_krb5_principal_set_realm(context, *out_princ, lpcfg_realm(lp_ctx));
+- if (ret) {
+- return ret;
+- }
+- }
++ if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT && canon) {
++ /*
++ * When requested to do so, ensure that the
++ * both realm values in the principal are set
++ * to the upper case, canonical realm
++ */
++ code = smb_krb5_make_principal(context,
++ out_princ,
++ lpcfg_realm(lp_ctx),
++ "krbtgt",
++ lpcfg_realm(lp_ctx),
++ NULL);
++ if (code != 0) {
++ return code;
++ }
++ smb_krb5_principal_set_type(context,
++ *out_princ,
++ KRB5_NT_SRV_INST);
+
+- } else if (ent_type == SAMBA_KDC_ENT_TYPE_ANY && in_princ == NULL) {
+- ret = smb_krb5_make_principal(context, out_princ, lpcfg_realm(lp_ctx), samAccountName, NULL);
+- if (ret) {
+- return ret;
+- }
+- } else if ((flags & SDB_F_FORCE_CANON) ||
+- ((flags & SDB_F_CANON) && (flags & SDB_F_FOR_AS_REQ))) {
++ return 0;
++ }
++
++ if ((canon && flags & (SDB_F_FORCE_CANON|SDB_F_FOR_AS_REQ)) ||
++ (ent_type == SAMBA_KDC_ENT_TYPE_ANY && in_princ == NULL)) {
+ /*
+ * SDB_F_CANON maps from the canonicalize flag in the
+ * packet, and has a different meaning between AS-REQ
+- * and TGS-REQ. We only change the principal in the AS-REQ case
++ * and TGS-REQ. We only change the principal in the
++ * AS-REQ case.
+ *
+- * The SDB_F_FORCE_CANON if for new MIT KDC code that wants
+- * the canonical name in all lookups, and takes care to
+- * canonicalize only when appropriate.
++ * The SDB_F_FORCE_CANON if for new MIT KDC code that
++ * wants the canonical name in all lookups, and takes
++ * care to canonicalize only when appropriate.
+ */
+- ret = smb_krb5_make_principal(context, out_princ, lpcfg_realm(lp_ctx), samAccountName, NULL);
+- if (ret) {
+- return ret;
+- }
+- } else {
+- ret = krb5_copy_principal(context, in_princ, out_princ);
+- if (ret) {
+- return ret;
+- }
+-
+- /* While we have copied the client principal, tests
+- * show that Win2k3 returns the 'corrected' realm, not
+- * the client-specified realm. This code attempts to
+- * replace the client principal's realm with the one
+- * we determine from our records */
++ code = smb_krb5_make_principal(context,
++ out_princ,
++ lpcfg_realm(lp_ctx),
++ samAccountName,
++ NULL);
++ return code;
++ }
+
+- /* this has to be with malloc() */
+- ret = smb_krb5_principal_set_realm(context, *out_princ, lpcfg_realm(lp_ctx));
+- if (ret) {
+- return ret;
+- }
++ /*
++ * For a krbtgt entry, this appears to be required regardless of the
++ * canonicalize flag from the client.
++ */
++ code = krb5_copy_principal(context, in_princ, out_princ);
++ if (code != 0) {
++ return code;
+ }
+
+- return 0;
++ /*
++ * While we have copied the client principal, tests show that Win2k3
++ * returns the 'corrected' realm, not the client-specified realm. This
++ * code attempts to replace the client principal's realm with the one
++ * we determine from our records
++ */
++ code = smb_krb5_principal_set_realm(context,
++ *out_princ,
++ lpcfg_realm(lp_ctx));
++
++ return code;
+ }
+
+ /*
+--
+2.25.1
+
+
+From 3cab62893668742781551dae6505558e47cf08b5 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 18 May 2022 16:56:01 +1200
+Subject: [PATCH 84/99] CVE-2022-2031 s4:kdc: Fix canonicalisation of
+ kadmin/changepw principal
+
+Since this principal goes through the samba_kdc_fetch_server() path,
+setting the canonicalisation flag would cause the principal to be
+replaced with the sAMAccountName; this meant requests to
+kadmin/changepw@REALM would result in a ticket to krbtgt@REALM. Now we
+properly handle canonicalisation for the kadmin/changepw principal.
+
+View with 'git show -b'.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Pair-Programmed-With: Andreas Schneider <asn@samba.org>
+Signed-off-by: Andreas Schneider <asn@samba.org>
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Adapted entry to entry_ex->entry; removed MIT KDC
+ 1.20-specific knownfails]
+---
+ selftest/knownfail.d/kadmin_changepw | 1 -
+ selftest/knownfail_heimdal_kdc | 2 -
+ source4/kdc/db-glue.c | 84 +++++++++++++++-------------
+ 3 files changed, 46 insertions(+), 41 deletions(-)
+ delete mode 100644 selftest/knownfail.d/kadmin_changepw
+
+diff --git selftest/knownfail.d/kadmin_changepw selftest/knownfail.d/kadmin_changepw
+deleted file mode 100644
+index 97c14793ea5..00000000000
+--- selftest/knownfail.d/kadmin_changepw
++++ /dev/null
+@@ -1 +0,0 @@
+-^samba4.blackbox.kpasswd.MIT kpasswd.change.user.password
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 5cd8615f6a9..49ab29f115d 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -274,8 +274,6 @@
+ #
+ # Kpasswd tests
+ #
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+diff --git source4/kdc/db-glue.c source4/kdc/db-glue.c
+index 385c118a073..d2d7136608e 100644
+--- source4/kdc/db-glue.c
++++ source4/kdc/db-glue.c
+@@ -830,6 +830,7 @@ static krb5_error_code samba_kdc_get_entry_principal(
+ const char *samAccountName,
+ enum samba_kdc_ent_type ent_type,
+ unsigned flags,
++ bool is_kadmin_changepw,
+ krb5_const_principal in_princ,
+ krb5_principal *out_princ)
+ {
+@@ -849,46 +850,52 @@ static krb5_error_code samba_kdc_get_entry_principal(
+ * fixed UPPER case realm, but the as-sent username
+ */
+
+- if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT && canon) {
+- /*
+- * When requested to do so, ensure that the
+- * both realm values in the principal are set
+- * to the upper case, canonical realm
+- */
+- code = smb_krb5_make_principal(context,
+- out_princ,
+- lpcfg_realm(lp_ctx),
+- "krbtgt",
+- lpcfg_realm(lp_ctx),
+- NULL);
+- if (code != 0) {
+- return code;
+- }
+- smb_krb5_principal_set_type(context,
+- *out_princ,
+- KRB5_NT_SRV_INST);
++ /*
++ * We need to ensure that the kadmin/changepw principal isn't able to
++ * issue krbtgt tickets, even if canonicalization is turned on.
++ */
++ if (!is_kadmin_changepw) {
++ if (ent_type == SAMBA_KDC_ENT_TYPE_KRBTGT && canon) {
++ /*
++ * When requested to do so, ensure that the
++ * both realm values in the principal are set
++ * to the upper case, canonical realm
++ */
++ code = smb_krb5_make_principal(context,
++ out_princ,
++ lpcfg_realm(lp_ctx),
++ "krbtgt",
++ lpcfg_realm(lp_ctx),
++ NULL);
++ if (code != 0) {
++ return code;
++ }
++ smb_krb5_principal_set_type(context,
++ *out_princ,
++ KRB5_NT_SRV_INST);
+
+- return 0;
+- }
++ return 0;
++ }
+
+- if ((canon && flags & (SDB_F_FORCE_CANON|SDB_F_FOR_AS_REQ)) ||
+- (ent_type == SAMBA_KDC_ENT_TYPE_ANY && in_princ == NULL)) {
+- /*
+- * SDB_F_CANON maps from the canonicalize flag in the
+- * packet, and has a different meaning between AS-REQ
+- * and TGS-REQ. We only change the principal in the
+- * AS-REQ case.
+- *
+- * The SDB_F_FORCE_CANON if for new MIT KDC code that
+- * wants the canonical name in all lookups, and takes
+- * care to canonicalize only when appropriate.
+- */
+- code = smb_krb5_make_principal(context,
+- out_princ,
+- lpcfg_realm(lp_ctx),
+- samAccountName,
+- NULL);
+- return code;
++ if ((canon && flags & (SDB_F_FORCE_CANON|SDB_F_FOR_AS_REQ)) ||
++ (ent_type == SAMBA_KDC_ENT_TYPE_ANY && in_princ == NULL)) {
++ /*
++ * SDB_F_CANON maps from the canonicalize flag in the
++ * packet, and has a different meaning between AS-REQ
++ * and TGS-REQ. We only change the principal in the
++ * AS-REQ case.
++ *
++ * The SDB_F_FORCE_CANON if for new MIT KDC code that
++ * wants the canonical name in all lookups, and takes
++ * care to canonicalize only when appropriate.
++ */
++ code = smb_krb5_make_principal(context,
++ out_princ,
++ lpcfg_realm(lp_ctx),
++ samAccountName,
++ NULL);
++ return code;
++ }
+ }
+
+ /*
+@@ -1194,6 +1201,7 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
+ samAccountName,
+ ent_type,
+ flags,
++ entry_ex->entry.flags.change_pw,
+ principal,
+ &entry_ex->entry.principal);
+ if (ret != 0) {
+--
+2.25.1
+
+
+From 531e7b596d35785bee61f3b4289e38ece1530f94 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 24 May 2022 17:53:49 +1200
+Subject: [PATCH 85/99] CVE-2022-2031 s4:kdc: Limit kpasswd ticket lifetime to
+ two minutes or less
+
+This matches the behaviour of Windows.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Adapted entry to entry_ex->entry; included
+ samba_kdc.h header file]
+
+[jsutton@samba.org Fixed conflicts]
+---
+ selftest/knownfail_heimdal_kdc | 1 -
+ selftest/knownfail_mit_kdc | 1 -
+ source4/kdc/db-glue.c | 5 +++++
+ source4/kdc/mit-kdb/kdb_samba_principals.c | 2 +-
+ source4/kdc/samba_kdc.h | 2 ++
+ 5 files changed, 8 insertions(+), 3 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 49ab29f115d..387ccea3ba7 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -275,7 +275,6 @@
+ # Kpasswd tests
+ #
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 46b0f1fa9ed..c2a31b4a140 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -580,7 +580,6 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ #
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_lifetime.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+diff --git source4/kdc/db-glue.c source4/kdc/db-glue.c
+index d2d7136608e..073ec83c8cf 100644
+--- source4/kdc/db-glue.c
++++ source4/kdc/db-glue.c
+@@ -1226,6 +1226,11 @@ static krb5_error_code samba_kdc_message2entry(krb5_context context,
+ kdc_db_ctx->policy.usr_tkt_lifetime);
+ }
+
++ if (entry_ex->entry.flags.change_pw) {
++ /* Limit lifetime of kpasswd tickets to two minutes or less. */
++ *entry_ex->entry.max_life = MIN(*entry_ex->entry.max_life, CHANGEPW_LIFETIME);
++ }
++
+ entry_ex->entry.max_renew = malloc(sizeof(*entry_ex->entry.max_life));
+ if (entry_ex->entry.max_renew == NULL) {
+ ret = ENOMEM;
+diff --git source4/kdc/mit-kdb/kdb_samba_principals.c source4/kdc/mit-kdb/kdb_samba_principals.c
+index cc67c2392be..2059ffa855e 100644
+--- source4/kdc/mit-kdb/kdb_samba_principals.c
++++ source4/kdc/mit-kdb/kdb_samba_principals.c
+@@ -27,11 +27,11 @@
+ #include <profile.h>
+ #include <kdb.h>
+
++#include "kdc/samba_kdc.h"
+ #include "kdc/mit_samba.h"
+ #include "kdb_samba.h"
+
+ #define ADMIN_LIFETIME 60*60*3 /* 3 hours */
+-#define CHANGEPW_LIFETIME 60*5 /* 5 minutes */
+
+ krb5_error_code ks_get_principal(krb5_context context,
+ krb5_const_principal principal,
+diff --git source4/kdc/samba_kdc.h source4/kdc/samba_kdc.h
+index e228a82ce6a..8010d7c35ed 100644
+--- source4/kdc/samba_kdc.h
++++ source4/kdc/samba_kdc.h
+@@ -62,4 +62,6 @@ struct samba_kdc_entry {
+
+ extern struct hdb_method hdb_samba4_interface;
+
++#define CHANGEPW_LIFETIME 60*2 /* 2 minutes */
++
+ #endif /* _SAMBA_KDC_H_ */
+--
+2.25.1
+
+
+From abdac4241dd08dd90a08db877edd799f3833c2b4 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Mon, 30 May 2022 19:18:17 +1200
+Subject: [PATCH 86/99] CVE-2022-2031 s4:kdc: Reject tickets during the last
+ two minutes of their life
+
+For Heimdal, this now matches the behaviour of Windows. The object of
+this requirement is to ensure we don't allow kpasswd tickets, not having
+a lifetime of more than two minutes, to be passed off as TGTs.
+
+An existing requirement for TGTs to contain a REQUESTER_SID PAC buffer
+suffices to prevent kpasswd ticket misuse, so this is just an additional
+precaution on top.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org As we don't have access to the ticket or the request
+ in the plugin, rewrote check directly in Heimdal KDC]
+---
+ selftest/knownfail_heimdal_kdc | 1 -
+ source4/heimdal/kdc/krb5tgs.c | 19 ++++++++++++++++++-
+ 2 files changed, 18 insertions(+), 2 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 387ccea3ba7..afb9bcf1209 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -275,7 +275,6 @@
+ # Kpasswd tests
+ #
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+diff --git source4/heimdal/kdc/krb5tgs.c source4/heimdal/kdc/krb5tgs.c
+index 38dba8493ae..15be136496f 100644
+--- source4/heimdal/kdc/krb5tgs.c
++++ source4/heimdal/kdc/krb5tgs.c
+@@ -33,6 +33,9 @@
+
+ #include "kdc_locl.h"
+
++/* Awful hack to get access to 'struct samba_kdc_entry'. */
++#include "../../kdc/samba_kdc.h"
++
+ /*
+ * return the realm of a krbtgt-ticket or NULL
+ */
+@@ -130,6 +133,7 @@ check_PAC(krb5_context context,
+ static krb5_error_code
+ check_tgs_flags(krb5_context context,
+ krb5_kdc_configuration *config,
++ const hdb_entry_ex *krbtgt_in,
+ KDC_REQ_BODY *b, const EncTicketPart *tgt, EncTicketPart *et)
+ {
+ KDCOptions f = b->kdc_options;
+@@ -244,6 +248,17 @@ check_tgs_flags(krb5_context context,
+ et->endtime = min(*et->renew_till, et->endtime);
+ }
+
++ if (tgt->endtime - kdc_time <= CHANGEPW_LIFETIME) {
++ /* Check that the ticket has not arrived across a trust. */
++ const struct samba_kdc_entry *skdc_entry = krbtgt_in->ctx;
++ if (!skdc_entry->is_trust) {
++ /* This may be a kpasswd ticket rather than a TGT, so don't accept it. */
++ kdc_log(context, config, 0,
++ "Ticket is not a ticket-granting ticket");
++ return KRB5KRB_AP_ERR_TKT_EXPIRED;
++ }
++ }
++
+ #if 0
+ /* checks for excess flags */
+ if(f.request_anonymous && !config->allow_anonymous){
+@@ -510,6 +525,7 @@ tgs_make_reply(krb5_context context,
+ hdb_entry_ex *client,
+ krb5_principal client_principal,
+ const char *tgt_realm,
++ const hdb_entry_ex *krbtgt_in,
+ hdb_entry_ex *krbtgt,
+ krb5_pac mspac,
+ uint16_t rodc_id,
+@@ -538,7 +554,7 @@ tgs_make_reply(krb5_context context,
+ ALLOC(et.starttime);
+ *et.starttime = kdc_time;
+
+- ret = check_tgs_flags(context, config, b, tgt, &et);
++ ret = check_tgs_flags(context, config, krbtgt_in, b, tgt, &et);
+ if(ret)
+ goto out;
+
+@@ -2129,6 +2145,7 @@ server_lookup:
+ client,
+ cp,
+ tgt_realm,
++ krbtgt,
+ krbtgt_out,
+ mspac,
+ rodc_id,
+--
+2.25.1
+
+
+From 389851bcf399f9511e2cb797350c37ce91aa5849 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Tue, 14 Jun 2022 15:23:55 +1200
+Subject: [PATCH 87/99] CVE-2022-2031 tests/krb5: Test truncated forms of
+ server principals
+
+We should not be able to use krb@REALM instead of krbtgt@REALM.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Fixed conflicts due to having older version of
+ _run_as_req_enc_timestamp()]
+---
+ python/samba/tests/krb5/as_req_tests.py | 40 ++++++++++++++++++++++---
+ selftest/knownfail_heimdal_kdc | 4 +++
+ selftest/knownfail_mit_kdc | 4 +++
+ 3 files changed, 44 insertions(+), 4 deletions(-)
+
+diff --git python/samba/tests/krb5/as_req_tests.py python/samba/tests/krb5/as_req_tests.py
+index 315720f85d6..054a49b64aa 100755
+--- python/samba/tests/krb5/as_req_tests.py
++++ python/samba/tests/krb5/as_req_tests.py
+@@ -27,6 +27,7 @@ from samba.tests.krb5.kdc_base_test import KDCBaseTest
+ import samba.tests.krb5.kcrypto as kcrypto
+ import samba.tests.krb5.rfc4120_pyasn1 as krb5_asn1
+ from samba.tests.krb5.rfc4120_constants import (
++ KDC_ERR_S_PRINCIPAL_UNKNOWN,
+ KDC_ERR_ETYPE_NOSUPP,
+ KDC_ERR_PREAUTH_REQUIRED,
+ KU_PA_ENC_TIMESTAMP,
+@@ -40,7 +41,8 @@ global_hexdump = False
+
+
+ class AsReqBaseTest(KDCBaseTest):
+- def _run_as_req_enc_timestamp(self, client_creds):
++ def _run_as_req_enc_timestamp(self, client_creds, sname=None,
++ expected_error=None):
+ client_account = client_creds.get_username()
+ client_as_etypes = self.get_default_enctypes()
+ client_kvno = client_creds.get_kvno()
+@@ -50,8 +52,9 @@ class AsReqBaseTest(KDCBaseTest):
+
+ cname = self.PrincipalName_create(name_type=NT_PRINCIPAL,
+ names=[client_account])
+- sname = self.PrincipalName_create(name_type=NT_SRV_INST,
+- names=[krbtgt_account, realm])
++ if sname is None:
++ sname = self.PrincipalName_create(name_type=NT_SRV_INST,
++ names=[krbtgt_account, realm])
+
+ expected_crealm = realm
+ expected_cname = cname
+@@ -63,7 +66,10 @@ class AsReqBaseTest(KDCBaseTest):
+
+ initial_etypes = client_as_etypes
+ initial_kdc_options = krb5_asn1.KDCOptions('forwardable')
+- initial_error_mode = KDC_ERR_PREAUTH_REQUIRED
++ if expected_error is not None:
++ initial_error_mode = expected_error
++ else:
++ initial_error_mode = KDC_ERR_PREAUTH_REQUIRED
+
+ rep, kdc_exchange_dict = self._test_as_exchange(cname,
+ realm,
+@@ -80,6 +86,10 @@ class AsReqBaseTest(KDCBaseTest):
+ None,
+ initial_kdc_options,
+ pac_request=True)
++
++ if expected_error is not None:
++ return None
++
+ etype_info2 = kdc_exchange_dict['preauth_etype_info2']
+ self.assertIsNotNone(etype_info2)
+
+@@ -209,6 +219,28 @@ class AsReqKerberosTests(AsReqBaseTest):
+ client_creds = self.get_mach_creds()
+ self._run_as_req_enc_timestamp(client_creds)
+
++ # Ensure we can't use truncated well-known principals such as krb@REALM
++ # instead of krbtgt@REALM.
++ def test_krbtgt_wrong_principal(self):
++ client_creds = self.get_client_creds()
++
++ krbtgt_creds = self.get_krbtgt_creds()
++
++ krbtgt_account = krbtgt_creds.get_username()
++ realm = krbtgt_creds.get_realm()
++
++ # Truncate the name of the krbtgt principal.
++ krbtgt_account = krbtgt_account[:3]
++
++ wrong_krbtgt_princ = self.PrincipalName_create(
++ name_type=NT_SRV_INST,
++ names=[krbtgt_account, realm])
++
++ self._run_as_req_enc_timestamp(
++ client_creds,
++ sname=wrong_krbtgt_princ,
++ expected_error=KDC_ERR_S_PRINCIPAL_UNKNOWN)
++
+
+ if __name__ == "__main__":
+ global_asn1_print = False
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index afb9bcf1209..dbfff5784e6 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -278,3 +278,7 @@
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
++#
++# AS-REQ tests
++#
++^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_krbtgt_wrong_principal\(
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index c2a31b4a140..0f90ea10299 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -583,3 +583,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
++#
++# AS-REQ tests
++#
++^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_krbtgt_wrong_principal\(
+--
+2.25.1
+
+
+From d40593be83144713cfc43e4eb1c7bc2d925a0da0 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 25 May 2022 20:00:55 +1200
+Subject: [PATCH 88/99] CVE-2022-2031 s4:kdc: Don't use strncmp to compare
+ principal components
+
+We would only compare the first 'n' characters, where 'n' is the length
+of the principal component string, so 'k@REALM' would erroneously be
+considered equal to 'krbtgt@REALM'.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ selftest/knownfail_heimdal_kdc | 4 ----
+ selftest/knownfail_mit_kdc | 4 ----
+ source4/kdc/db-glue.c | 27 ++++++++++++++++++++++-----
+ 3 files changed, 22 insertions(+), 13 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index dbfff5784e6..afb9bcf1209 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -278,7 +278,3 @@
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+-#
+-# AS-REQ tests
+-#
+-^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_krbtgt_wrong_principal\(
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 0f90ea10299..c2a31b4a140 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -583,7 +583,3 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+-#
+-# AS-REQ tests
+-#
+-^samba.tests.krb5.as_req_tests.samba.tests.krb5.as_req_tests.AsReqKerberosTests.test_krbtgt_wrong_principal\(
+diff --git source4/kdc/db-glue.c source4/kdc/db-glue.c
+index 073ec83c8cf..cfa2097acbd 100644
+--- source4/kdc/db-glue.c
++++ source4/kdc/db-glue.c
+@@ -769,15 +769,19 @@ static int principal_comp_strcmp_int(krb5_context context,
+ bool do_strcasecmp)
+ {
+ const char *p;
+- size_t len;
+
+ #if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING)
+ p = krb5_principal_get_comp_string(context, principal, component);
+ if (p == NULL) {
+ return -1;
+ }
+- len = strlen(p);
++ if (do_strcasecmp) {
++ return strcasecmp(p, string);
++ } else {
++ return strcmp(p, string);
++ }
+ #else
++ size_t len;
+ krb5_data *d;
+ if (component >= krb5_princ_size(context, principal)) {
+ return -1;
+@@ -789,13 +793,26 @@ static int principal_comp_strcmp_int(krb5_context context,
+ }
+
+ p = d->data;
+- len = d->length;
+-#endif
++
++ len = strlen(string);
++
++ /*
++ * We explicitly return -1 or 1. Subtracting of the two lengths might
++ * give the wrong result if the result overflows or loses data when
++ * narrowed to int.
++ */
++ if (d->length < len) {
++ return -1;
++ } else if (d->length > len) {
++ return 1;
++ }
++
+ if (do_strcasecmp) {
+ return strncasecmp(p, string, len);
+ } else {
+- return strncmp(p, string, len);
++ return memcmp(p, string, len);
+ }
++#endif
+ }
+
+ static int principal_comp_strcasecmp(krb5_context context,
+--
+2.25.1
+
+
+From 42ba919c06c24c42ef123304de0c2ca8c689591a Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 26 May 2022 16:36:30 +1200
+Subject: [PATCH 89/99] CVE-2022-32744 s4:kdc: Rename keytab_name ->
+ kpasswd_keytab_name
+
+This makes explicitly clear the purpose of this keytab.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Fixed conflicts due to lacking HDBGET support]
+---
+ source4/kdc/kdc-heimdal.c | 4 ++--
+ source4/kdc/kdc-server.h | 2 +-
+ source4/kdc/kdc-service-mit.c | 4 ++--
+ source4/kdc/kpasswd-service.c | 2 +-
+ 4 files changed, 6 insertions(+), 6 deletions(-)
+
+diff --git source4/kdc/kdc-heimdal.c source4/kdc/kdc-heimdal.c
+index ba74df4f2ec..a4c845b62f8 100644
+--- source4/kdc/kdc-heimdal.c
++++ source4/kdc/kdc-heimdal.c
+@@ -444,8 +444,8 @@ static void kdc_post_fork(struct task_server *task, struct process_details *pd)
+ return;
+ }
+
+- kdc->keytab_name = talloc_asprintf(kdc, "HDB:samba4&%p", kdc->base_ctx);
+- if (kdc->keytab_name == NULL) {
++ kdc->kpasswd_keytab_name = talloc_asprintf(kdc, "HDB:samba4&%p", kdc->base_ctx);
++ if (kdc->kpasswd_keytab_name == NULL) {
+ task_server_terminate(task,
+ "kdc: Failed to set keytab name",
+ true);
+diff --git source4/kdc/kdc-server.h source4/kdc/kdc-server.h
+index fd883c2e4b4..89b30f122f5 100644
+--- source4/kdc/kdc-server.h
++++ source4/kdc/kdc-server.h
+@@ -40,7 +40,7 @@ struct kdc_server {
+ struct ldb_context *samdb;
+ bool am_rodc;
+ uint32_t proxy_timeout;
+- const char *keytab_name;
++ const char *kpasswd_keytab_name;
+ void *private_data;
+ };
+
+diff --git source4/kdc/kdc-service-mit.c source4/kdc/kdc-service-mit.c
+index 5d4180aa7cc..22663b6ecc8 100644
+--- source4/kdc/kdc-service-mit.c
++++ source4/kdc/kdc-service-mit.c
+@@ -291,8 +291,8 @@ NTSTATUS mitkdc_task_init(struct task_server *task)
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+- kdc->keytab_name = talloc_asprintf(kdc, "KDB:");
+- if (kdc->keytab_name == NULL) {
++ kdc->kpasswd_keytab_name = talloc_asprintf(kdc, "KDB:");
++ if (kdc->kpasswd_keytab_name == NULL) {
+ task_server_terminate(task,
+ "KDC: Out of memory",
+ true);
+diff --git source4/kdc/kpasswd-service.c source4/kdc/kpasswd-service.c
+index b4706de1ad7..0d2acd8d9e8 100644
+--- source4/kdc/kpasswd-service.c
++++ source4/kdc/kpasswd-service.c
+@@ -167,7 +167,7 @@ kdc_code kpasswd_process(struct kdc_server *kdc,
+
+ rv = cli_credentials_set_keytab_name(server_credentials,
+ kdc->task->lp_ctx,
+- kdc->keytab_name,
++ kdc->kpasswd_keytab_name,
+ CRED_SPECIFIED);
+ if (rv != 0) {
+ DBG_ERR("Failed to set credentials keytab name\n");
+--
+2.25.1
+
+
+From 997f50c66471071efb8e02d8efbe4bf5d932e7ee Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Wed, 8 Jun 2022 13:53:29 +1200
+Subject: [PATCH 90/99] s4:kdc: Remove kadmin mode from HDB plugin
+
+It appears we no longer require it.
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ source4/kdc/hdb-samba4-plugin.c | 35 +++++++--------------------------
+ 1 file changed, 7 insertions(+), 28 deletions(-)
+
+diff --git source4/kdc/hdb-samba4-plugin.c source4/kdc/hdb-samba4-plugin.c
+index 6f76124995d..4b90a766f76 100644
+--- source4/kdc/hdb-samba4-plugin.c
++++ source4/kdc/hdb-samba4-plugin.c
+@@ -21,40 +21,20 @@
+
+ #include "includes.h"
+ #include "kdc/kdc-glue.h"
+-#include "kdc/db-glue.h"
+-#include "lib/util/samba_util.h"
+ #include "lib/param/param.h"
+-#include "source4/lib/events/events.h"
+
+ static krb5_error_code hdb_samba4_create(krb5_context context, struct HDB **db, const char *arg)
+ {
+ NTSTATUS nt_status;
+- void *ptr;
+- struct samba_kdc_base_context *base_ctx;
+-
+- if (sscanf(arg, "&%p", &ptr) == 1) {
+- base_ctx = talloc_get_type_abort(ptr, struct samba_kdc_base_context);
+- } else if (arg[0] == '\0' || file_exist(arg)) {
+- /* This mode for use in kadmin, rather than in Samba */
+-
+- setup_logging("hdb_samba4", DEBUG_DEFAULT_STDERR);
+-
+- base_ctx = talloc_zero(NULL, struct samba_kdc_base_context);
+- if (!base_ctx) {
+- return ENOMEM;
+- }
+-
+- base_ctx->ev_ctx = s4_event_context_init(base_ctx);
+- base_ctx->lp_ctx = loadparm_init_global(false);
+- if (arg[0]) {
+- lpcfg_load(base_ctx->lp_ctx, arg);
+- } else {
+- lpcfg_load_default(base_ctx->lp_ctx);
+- }
+- } else {
++ void *ptr = NULL;
++ struct samba_kdc_base_context *base_ctx = NULL;
++
++ if (sscanf(arg, "&%p", &ptr) != 1) {
+ return EINVAL;
+ }
+
++ base_ctx = talloc_get_type_abort(ptr, struct samba_kdc_base_context);
++
+ /* The global kdc_mem_ctx and kdc_lp_ctx, Disgusting, ugly hack, but it means one less private hook */
+ nt_status = hdb_samba4_create_kdc(base_ctx, context, db);
+
+@@ -90,8 +70,7 @@ static void hdb_samba4_fini(void *ctx)
+
+ /* Only used in the hdb-backed keytab code
+ * for a keytab of 'samba4&<address>' or samba4, to find
+- * kpasswd's key in the main DB, and to
+- * copy all the keys into a file (libnet_keytab_export)
++ * kpasswd's key in the main DB
+ *
+ * The <address> is the string form of a pointer to a talloced struct hdb_samba_context
+ */
+--
+2.25.1
+
+
+From c0c4b7a4bd229bd36d586faec6249baaba8e7adc Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 26 May 2022 16:39:20 +1200
+Subject: [PATCH 91/99] CVE-2022-32744 s4:kdc: Modify HDB plugin to only look
+ up kpasswd principal
+
+This plugin is now only used by the kpasswd service. Thus, ensuring we
+only look up the kadmin/changepw principal means we can't be fooled into
+accepting tickets for other service principals. We make sure not to
+specify a specific kvno, to ensure that we do not accept RODC-issued
+tickets.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Fixed knownfail conflicts]
+
+[jsutton@samba.org Renamed entry to entry_ex; fixed knownfail conflicts;
+ retained knownfail for test_kpasswd_from_rodc which now causes the KDC
+ to panic]
+---
+ selftest/knownfail_heimdal_kdc | 3 --
+ source4/kdc/hdb-samba4-plugin.c | 2 +-
+ source4/kdc/hdb-samba4.c | 66 +++++++++++++++++++++++++++++++++
+ source4/kdc/kdc-glue.h | 3 ++
+ 4 files changed, 70 insertions(+), 4 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index afb9bcf1209..0d93253f999 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -275,6 +275,3 @@
+ # Kpasswd tests
+ #
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+diff --git source4/kdc/hdb-samba4-plugin.c source4/kdc/hdb-samba4-plugin.c
+index 4b90a766f76..dba25e825de 100644
+--- source4/kdc/hdb-samba4-plugin.c
++++ source4/kdc/hdb-samba4-plugin.c
+@@ -36,7 +36,7 @@ static krb5_error_code hdb_samba4_create(krb5_context context, struct HDB **db,
+ base_ctx = talloc_get_type_abort(ptr, struct samba_kdc_base_context);
+
+ /* The global kdc_mem_ctx and kdc_lp_ctx, Disgusting, ugly hack, but it means one less private hook */
+- nt_status = hdb_samba4_create_kdc(base_ctx, context, db);
++ nt_status = hdb_samba4_kpasswd_create_kdc(base_ctx, context, db);
+
+ if (NT_STATUS_IS_OK(nt_status)) {
+ return 0;
+diff --git source4/kdc/hdb-samba4.c source4/kdc/hdb-samba4.c
+index 43e836f8360..a8aae50b5b0 100644
+--- source4/kdc/hdb-samba4.c
++++ source4/kdc/hdb-samba4.c
+@@ -136,6 +136,47 @@ static krb5_error_code hdb_samba4_fetch_kvno(krb5_context context, HDB *db,
+ return code;
+ }
+
++static krb5_error_code hdb_samba4_kpasswd_fetch_kvno(krb5_context context, HDB *db,
++ krb5_const_principal _principal,
++ unsigned flags,
++ krb5_kvno _kvno,
++ hdb_entry_ex *entry_ex)
++{
++ struct samba_kdc_db_context *kdc_db_ctx = NULL;
++ krb5_error_code ret;
++ krb5_principal kpasswd_principal = NULL;
++
++ kdc_db_ctx = talloc_get_type_abort(db->hdb_db,
++ struct samba_kdc_db_context);
++
++ ret = smb_krb5_make_principal(context, &kpasswd_principal,
++ lpcfg_realm(kdc_db_ctx->lp_ctx),
++ "kadmin", "changepw",
++ NULL);
++ if (ret) {
++ return ret;
++ }
++ smb_krb5_principal_set_type(context, kpasswd_principal, KRB5_NT_SRV_INST);
++
++ /*
++ * For the kpasswd service, always ensure we get the latest kvno. This
++ * also means we (correctly) refuse RODC-issued tickets.
++ */
++ flags &= ~HDB_F_KVNO_SPECIFIED;
++
++ /* Don't bother looking up a client or krbtgt. */
++ flags &= ~(SDB_F_GET_CLIENT|SDB_F_GET_KRBTGT);
++
++ ret = hdb_samba4_fetch_kvno(context, db,
++ kpasswd_principal,
++ flags,
++ 0,
++ entry_ex);
++
++ krb5_free_principal(context, kpasswd_principal);
++ return ret;
++}
++
+ static krb5_error_code hdb_samba4_firstkey(krb5_context context, HDB *db, unsigned flags,
+ hdb_entry_ex *entry)
+ {
+@@ -194,6 +235,14 @@ static krb5_error_code hdb_samba4_nextkey(krb5_context context, HDB *db, unsigne
+ return ret;
+ }
+
++static krb5_error_code hdb_samba4_nextkey_panic(krb5_context context, HDB *db,
++ unsigned flags,
++ hdb_entry_ex *entry)
++{
++ DBG_ERR("Attempt to iterate kpasswd keytab => PANIC\n");
++ smb_panic("hdb_samba4_nextkey_panic: Attempt to iterate kpasswd keytab");
++}
++
+ static krb5_error_code hdb_samba4_destroy(krb5_context context, HDB *db)
+ {
+ talloc_free(db);
+@@ -522,3 +571,20 @@ NTSTATUS hdb_samba4_create_kdc(struct samba_kdc_base_context *base_ctx,
+
+ return NT_STATUS_OK;
+ }
++
++NTSTATUS hdb_samba4_kpasswd_create_kdc(struct samba_kdc_base_context *base_ctx,
++ krb5_context context, struct HDB **db)
++{
++ NTSTATUS nt_status;
++
++ nt_status = hdb_samba4_create_kdc(base_ctx, context, db);
++ if (!NT_STATUS_IS_OK(nt_status)) {
++ return nt_status;
++ }
++
++ (*db)->hdb_fetch_kvno = hdb_samba4_kpasswd_fetch_kvno;
++ (*db)->hdb_firstkey = hdb_samba4_nextkey_panic;
++ (*db)->hdb_nextkey = hdb_samba4_nextkey_panic;
++
++ return NT_STATUS_OK;
++}
+diff --git source4/kdc/kdc-glue.h source4/kdc/kdc-glue.h
+index c083b8c6429..ff8684e1666 100644
+--- source4/kdc/kdc-glue.h
++++ source4/kdc/kdc-glue.h
+@@ -45,6 +45,9 @@ kdc_code kpasswdd_process(struct kdc_server *kdc,
+ NTSTATUS hdb_samba4_create_kdc(struct samba_kdc_base_context *base_ctx,
+ krb5_context context, struct HDB **db);
+
++NTSTATUS hdb_samba4_kpasswd_create_kdc(struct samba_kdc_base_context *base_ctx,
++ krb5_context context, struct HDB **db);
++
+ /* from kdc-glue.c */
+ int kdc_check_pac(krb5_context krb5_context,
+ DATA_BLOB server_sig,
+--
+2.25.1
+
+
+From 340181bc1100fa31c63af88214a3d8328b944fe9 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Mon, 30 May 2022 19:16:02 +1200
+Subject: [PATCH 92/99] CVE-2022-32744 s4:kpasswd: Ensure we pass the kpasswd
+ server principal into krb5_rd_req_ctx()
+
+To ensure that, when decrypting the kpasswd ticket, we look up the
+correct principal and don't trust the sname from the ticket, we should
+pass the principal name of the kpasswd service into krb5_rd_req_ctx().
+However, gensec_krb5_update_internal() will pass in NULL unless the
+principal in our credentials is CRED_SPECIFIED.
+
+At present, our principal will be considered obtained as CRED_SMB_CONF
+(from the cli_credentials_set_conf() a few lines up), so we explicitly
+set the realm again, but this time as CRED_SPECIFIED. Now the value of
+server_in_keytab that we provide to smb_krb5_rd_req_decoded() will not
+be NULL.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Removed knownfail as KDC no longer panics]
+---
+ selftest/knownfail_heimdal_kdc | 4 ----
+ selftest/knownfail_mit_kdc | 2 --
+ source4/kdc/kpasswd-service.c | 30 ++++++++++++++++++++++++++++++
+ 3 files changed, 30 insertions(+), 6 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 0d93253f999..424a8b81c38 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -271,7 +271,3 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+-#
+-# Kpasswd tests
+-#
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_from_rodc.ad_dc
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index c2a31b4a140..0d2f5bab6d2 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -581,5 +581,3 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_server.ad_dc
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_wrong_key_service.ad_dc
+diff --git source4/kdc/kpasswd-service.c source4/kdc/kpasswd-service.c
+index 0d2acd8d9e8..b6400be0c49 100644
+--- source4/kdc/kpasswd-service.c
++++ source4/kdc/kpasswd-service.c
+@@ -29,6 +29,7 @@
+ #include "kdc/kdc-server.h"
+ #include "kdc/kpasswd-service.h"
+ #include "kdc/kpasswd-helper.h"
++#include "param/param.h"
+
+ #define HEADER_LEN 6
+ #ifndef RFC3244_VERSION
+@@ -158,6 +159,20 @@ kdc_code kpasswd_process(struct kdc_server *kdc,
+
+ cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx);
+
++ /*
++ * After calling cli_credentials_set_conf(), explicitly set the realm
++ * with CRED_SPECIFIED. We need to do this so the result of
++ * principal_from_credentials() called from the gensec layer is
++ * CRED_SPECIFIED rather than CRED_SMB_CONF, avoiding a fallback to
++ * match-by-key (very undesirable in this case).
++ */
++ ok = cli_credentials_set_realm(server_credentials,
++ lpcfg_realm(kdc->task->lp_ctx),
++ CRED_SPECIFIED);
++ if (!ok) {
++ goto done;
++ }
++
+ ok = cli_credentials_set_username(server_credentials,
+ "kadmin/changepw",
+ CRED_SPECIFIED);
+@@ -165,6 +180,21 @@ kdc_code kpasswd_process(struct kdc_server *kdc,
+ goto done;
+ }
+
++ /* Check that the server principal is indeed CRED_SPECIFIED. */
++ {
++ char *principal = NULL;
++ enum credentials_obtained obtained;
++
++ principal = cli_credentials_get_principal_and_obtained(server_credentials,
++ tmp_ctx,
++ &obtained);
++ if (obtained < CRED_SPECIFIED) {
++ goto done;
++ }
++
++ TALLOC_FREE(principal);
++ }
++
+ rv = cli_credentials_set_keytab_name(server_credentials,
+ kdc->task->lp_ctx,
+ kdc->kpasswd_keytab_name,
+--
+2.25.1
+
+
+From 95afbc2da9b541fb8f2eebdcd411f5873d1675ac Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Fri, 10 Jun 2022 19:17:11 +1200
+Subject: [PATCH 93/99] CVE-2022-2031 tests/krb5: Add test that we cannot
+ provide a TGT to kpasswd
+
+The kpasswd service should require a kpasswd service ticket, and
+disallow TGTs.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Fixed knownfail conflicts]
+
+[jsutton@samba.org Fixed knownfail conflicts]
+---
+ python/samba/tests/krb5/kpasswd_tests.py | 28 ++++++++++++++++++++++++
+ selftest/knownfail_heimdal_kdc | 4 ++++
+ selftest/knownfail_mit_kdc | 4 ++++
+ 3 files changed, 36 insertions(+)
+
+diff --git python/samba/tests/krb5/kpasswd_tests.py python/samba/tests/krb5/kpasswd_tests.py
+index 3a6c7d818dc..0db857f7bbd 100755
+--- python/samba/tests/krb5/kpasswd_tests.py
++++ python/samba/tests/krb5/kpasswd_tests.py
+@@ -31,6 +31,7 @@ from samba.tests.krb5.rfc4120_constants import (
+ KDC_ERR_TGT_REVOKED,
+ KDC_ERR_TKT_EXPIRED,
+ KPASSWD_ACCESSDENIED,
++ KPASSWD_AUTHERROR,
+ KPASSWD_HARDERROR,
+ KPASSWD_INITIAL_FLAG_NEEDED,
+ KPASSWD_MALFORMED,
+@@ -779,6 +780,33 @@ class KpasswdTests(KDCBaseTest):
+ self._make_tgs_request(creds, service_creds, ticket,
+ expect_error=False)
+
++ # Show that we cannot provide a TGT to kpasswd to change the password.
++ def test_kpasswd_tgt(self):
++ # Create an account for testing, and get a TGT.
++ creds = self._get_creds()
++ tgt = self.get_tgt(creds)
++
++ # Change the sname of the ticket to match that of kadmin/changepw.
++ tgt.set_sname(self.get_kpasswd_sname())
++
++ expected_code = KPASSWD_AUTHERROR
++ expected_msg = b'A TGT may not be used as a ticket to kpasswd'
++
++ # Set the password.
++ new_password = generate_random_password(32, 32)
++ self.kpasswd_exchange(tgt,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.SET)
++
++ # Change the password.
++ self.kpasswd_exchange(tgt,
++ new_password,
++ expected_code,
++ expected_msg,
++ mode=self.KpasswdMode.CHANGE)
++
+ # Test that kpasswd rejects requests with a service ticket.
+ def test_kpasswd_non_initial(self):
+ # Create an account for testing, and get a TGT.
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 424a8b81c38..42beccaed58 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -271,3 +271,7 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
++#
++# Kpasswd tests
++#
++^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_tgt.ad_dc
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 0d2f5bab6d2..9fc34e5d8db 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -581,3 +581,7 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
++#
++# Kpasswd tests
++#
++samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_tgt.ad_dc
+--
+2.25.1
+
+
+From 4b61092459b403b2945daa9082052366f3508b69 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Fri, 10 Jun 2022 19:18:07 +1200
+Subject: [PATCH 94/99] CVE-2022-2031 auth: Add ticket type field to
+ auth_user_info_dc and auth_session_info
+
+This field may be used to convey whether we were provided with a TGT or
+a non-TGT. We ensure both structures are zeroed out to avoid incorrect
+results being produced by an uninitialised field.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ auth/auth_sam_reply.c | 2 +-
+ auth/auth_util.c | 2 +-
+ librpc/idl/auth.idl | 23 +++++++++++++++++++++++
+ source4/auth/ntlm/auth_developer.c | 2 +-
+ source4/auth/sam.c | 2 +-
+ source4/auth/session.c | 2 ++
+ source4/auth/system_session.c | 6 +++---
+ 7 files changed, 32 insertions(+), 7 deletions(-)
+
+diff --git auth/auth_sam_reply.c auth/auth_sam_reply.c
+index b5b6362dc93..2e27e5715d1 100644
+--- auth/auth_sam_reply.c
++++ auth/auth_sam_reply.c
+@@ -416,7 +416,7 @@ NTSTATUS make_user_info_dc_netlogon_validation(TALLOC_CTX *mem_ctx,
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+- user_info_dc = talloc(mem_ctx, struct auth_user_info_dc);
++ user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
+ NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
+
+ /*
+diff --git auth/auth_util.c auth/auth_util.c
+index fe01babd107..ec9094d0f15 100644
+--- auth/auth_util.c
++++ auth/auth_util.c
+@@ -44,7 +44,7 @@ struct auth_session_info *copy_session_info(TALLOC_CTX *mem_ctx,
+ return NULL;
+ }
+
+- dst = talloc(mem_ctx, struct auth_session_info);
++ dst = talloc_zero(mem_ctx, struct auth_session_info);
+ if (dst == NULL) {
+ DBG_ERR("talloc failed\n");
+ TALLOC_FREE(frame);
+diff --git librpc/idl/auth.idl librpc/idl/auth.idl
+index 1092935b971..f7658cdde28 100644
+--- librpc/idl/auth.idl
++++ librpc/idl/auth.idl
+@@ -75,6 +75,26 @@ interface auth
+ [unique,charset(UTF8),string] char *sanitized_username;
+ } auth_user_info_unix;
+
++ /*
++ * If the user was authenticated with a Kerberos ticket, this indicates
++ * the type of the ticket; TGT, or non-TGT (i.e. service ticket). If
++ * unset, the type is unknown. This indicator is useful for the KDC and
++ * the kpasswd service, which share the same account and keys. By
++ * ensuring it is provided with the appopriate ticket type, each service
++ * avoids accepting a ticket meant for the other.
++ *
++ * The heuristic used to determine the type is the presence or absence
++ * of a REQUESTER_SID buffer in the PAC; we use its presence to assume
++ * we have a TGT. This heuristic will fail for older Samba versions and
++ * Windows prior to Nov. 2021 updates, which lack support for this
++ * buffer.
++ */
++ typedef enum {
++ TICKET_TYPE_UNKNOWN = 0,
++ TICKET_TYPE_TGT = 1,
++ TICKET_TYPE_NON_TGT = 2
++ } ticket_type;
++
+ /* This is the interim product of the auth subsystem, before
+ * privileges and local groups are handled */
+ typedef [public] struct {
+@@ -83,6 +103,7 @@ interface auth
+ auth_user_info *info;
+ [noprint] DATA_BLOB user_session_key;
+ [noprint] DATA_BLOB lm_session_key;
++ ticket_type ticket_type;
+ } auth_user_info_dc;
+
+ typedef [public] struct {
+@@ -112,6 +133,8 @@ interface auth
+ * We generate this in auth_generate_session_info()
+ */
+ GUID unique_session_token;
++
++ ticket_type ticket_type;
+ } auth_session_info;
+
+ typedef [public] struct {
+diff --git source4/auth/ntlm/auth_developer.c source4/auth/ntlm/auth_developer.c
+index 1823989c68d..6e92252d5c5 100644
+--- source4/auth/ntlm/auth_developer.c
++++ source4/auth/ntlm/auth_developer.c
+@@ -76,7 +76,7 @@ static NTSTATUS name_to_ntstatus_check_password(struct auth_method_context *ctx,
+ }
+ NT_STATUS_NOT_OK_RETURN(nt_status);
+
+- user_info_dc = talloc(mem_ctx, struct auth_user_info_dc);
++ user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
+ NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
+
+ /* This returns a pointer to a struct dom_sid, which is the
+diff --git source4/auth/sam.c source4/auth/sam.c
+index 8b233bab3ad..7c609655fcb 100644
+--- source4/auth/sam.c
++++ source4/auth/sam.c
+@@ -363,7 +363,7 @@ _PUBLIC_ NTSTATUS authsam_make_user_info_dc(TALLOC_CTX *mem_ctx,
+ TALLOC_CTX *tmp_ctx;
+ struct ldb_message_element *el;
+
+- user_info_dc = talloc(mem_ctx, struct auth_user_info_dc);
++ user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
+ NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
+
+ tmp_ctx = talloc_new(user_info_dc);
+diff --git source4/auth/session.c source4/auth/session.c
+index 8e44dcd24f1..d6e936dd1f1 100644
+--- source4/auth/session.c
++++ source4/auth/session.c
+@@ -222,6 +222,8 @@ _PUBLIC_ NTSTATUS auth_generate_session_info(TALLOC_CTX *mem_ctx,
+
+ session_info->credentials = NULL;
+
++ session_info->ticket_type = user_info_dc->ticket_type;
++
+ talloc_steal(mem_ctx, session_info);
+ *_session_info = session_info;
+ talloc_free(tmp_ctx);
+diff --git source4/auth/system_session.c source4/auth/system_session.c
+index 85b8f1c4edb..2518d654e8b 100644
+--- source4/auth/system_session.c
++++ source4/auth/system_session.c
+@@ -115,7 +115,7 @@ NTSTATUS auth_system_user_info_dc(TALLOC_CTX *mem_ctx, const char *netbios_name,
+ struct auth_user_info_dc *user_info_dc;
+ struct auth_user_info *info;
+
+- user_info_dc = talloc(mem_ctx, struct auth_user_info_dc);
++ user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
+ NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
+
+ /* This returns a pointer to a struct dom_sid, which is the
+@@ -191,7 +191,7 @@ static NTSTATUS auth_domain_admin_user_info_dc(TALLOC_CTX *mem_ctx,
+ struct auth_user_info_dc *user_info_dc;
+ struct auth_user_info *info;
+
+- user_info_dc = talloc(mem_ctx, struct auth_user_info_dc);
++ user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
+ NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
+
+ user_info_dc->num_sids = 7;
+@@ -356,7 +356,7 @@ _PUBLIC_ NTSTATUS auth_anonymous_user_info_dc(TALLOC_CTX *mem_ctx,
+ {
+ struct auth_user_info_dc *user_info_dc;
+ struct auth_user_info *info;
+- user_info_dc = talloc(mem_ctx, struct auth_user_info_dc);
++ user_info_dc = talloc_zero(mem_ctx, struct auth_user_info_dc);
+ NT_STATUS_HAVE_NO_MEMORY(user_info_dc);
+
+ /* This returns a pointer to a struct dom_sid, which is the
+--
+2.25.1
+
+
+From 89c6e36938c27b572573b06d1b35db210bfda99b Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Fri, 10 Jun 2022 19:18:35 +1200
+Subject: [PATCH 95/99] CVE-2022-2031 s4:auth: Use PAC to determine whether
+ ticket is a TGT
+
+We use the presence or absence of a REQUESTER_SID PAC buffer to
+determine whether the ticket is a TGT. We will later use this to reject
+TGTs where a service ticket is expected.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+---
+ source4/auth/kerberos/kerberos_pac.c | 44 ++++++++++++++++++++++++++++
+ 1 file changed, 44 insertions(+)
+
+diff --git source4/auth/kerberos/kerberos_pac.c source4/auth/kerberos/kerberos_pac.c
+index 54ef4d61b02..bd0ae20e007 100644
+--- source4/auth/kerberos/kerberos_pac.c
++++ source4/auth/kerberos/kerberos_pac.c
+@@ -282,6 +282,28 @@
+ return ret;
+ }
+
++static krb5_error_code kerberos_pac_buffer_present(krb5_context context,
++ const krb5_pac pac,
++ uint32_t type)
++{
++#ifdef SAMBA4_USES_HEIMDAL
++ return krb5_pac_get_buffer(context, pac, type, NULL);
++#else /* MIT */
++ krb5_error_code ret;
++ krb5_data data;
++
++ /*
++ * MIT won't let us pass NULL for the data parameter, so we are forced
++ * to allocate a new buffer and then immediately free it.
++ */
++ ret = krb5_pac_get_buffer(context, pac, type, &data);
++ if (ret == 0) {
++ krb5_free_data_contents(context, &data);
++ }
++ return ret;
++#endif /* SAMBA4_USES_HEIMDAL */
++}
++
+ krb5_error_code kerberos_pac_to_user_info_dc(TALLOC_CTX *mem_ctx,
+ krb5_pac pac,
+ krb5_context context,
+@@ -414,6 +436,28 @@ krb5_error_code kerberos_pac_to_user_info_dc(TALLOC_CTX *mem_ctx,
+ return EINVAL;
+ }
+ }
++
++ /*
++ * Based on the presence of a REQUESTER_SID PAC buffer, ascertain
++ * whether the ticket is a TGT. This helps the KDC and kpasswd service
++ * ensure they do not accept tickets meant for the other.
++ *
++ * This heuristic will fail for older Samba versions and Windows prior
++ * to Nov. 2021 updates, which lack support for the REQUESTER_SID PAC
++ * buffer.
++ */
++ ret = kerberos_pac_buffer_present(context, pac, PAC_TYPE_REQUESTER_SID);
++ if (ret == ENOENT) {
++ /* This probably isn't a TGT. */
++ user_info_dc_out->ticket_type = TICKET_TYPE_NON_TGT;
++ } else if (ret != 0) {
++ talloc_free(tmp_ctx);
++ return ret;
++ } else {
++ /* This probably is a TGT. */
++ user_info_dc_out->ticket_type = TICKET_TYPE_TGT;
++ }
++
+ *user_info_dc = user_info_dc_out;
+
+ return 0;
+--
+2.25.1
+
+
+From d5af460403d3949ba266f5c74f051247cd7ce752 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Fri, 10 Jun 2022 19:18:53 +1200
+Subject: [PATCH 96/99] CVE-2022-2031 s4:kpasswd: Do not accept TGTs as kpasswd
+ tickets
+
+If TGTs can be used as kpasswd tickets, the two-minute lifetime of a
+authentic kpasswd ticket may be bypassed. Furthermore, kpasswd tickets
+are not supposed to be cached, but using this flaw, a stolen credentials
+cache containing a TGT may be used to change that account's password,
+and thus is made more valuable to an attacker.
+
+Since all TGTs should be issued with a REQUESTER_SID PAC buffer, and
+service tickets without it, we assert the absence of this buffer to
+ensure we're not accepting a TGT.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15049
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn@samba.org>
+
+[jsutton@samba.org Fixed knownfail conflicts]
+
+[jsutton@samba.org Fixed knownfail conflicts]
+---
+ selftest/knownfail_heimdal_kdc | 4 ----
+ selftest/knownfail_mit_kdc | 4 ----
+ source4/kdc/kpasswd-helper.c | 20 ++++++++++++++++++++
+ source4/kdc/kpasswd-helper.h | 2 ++
+ source4/kdc/kpasswd-service-heimdal.c | 13 +++++++++++++
+ source4/kdc/kpasswd-service-mit.c | 13 +++++++++++++
+ 6 files changed, 48 insertions(+), 8 deletions(-)
+
+diff --git selftest/knownfail_heimdal_kdc selftest/knownfail_heimdal_kdc
+index 42beccaed58..424a8b81c38 100644
+--- selftest/knownfail_heimdal_kdc
++++ selftest/knownfail_heimdal_kdc
+@@ -271,7 +271,3 @@
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_service_ticket
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_existing
+ ^samba.tests.krb5.kdc_tgs_tests.samba.tests.krb5.kdc_tgs_tests.KdcTgsTests.test_fast_sid_mismatch_nonexisting
+-#
+-# Kpasswd tests
+-#
+-^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_tgt.ad_dc
+diff --git selftest/knownfail_mit_kdc selftest/knownfail_mit_kdc
+index 9fc34e5d8db..0d2f5bab6d2 100644
+--- selftest/knownfail_mit_kdc
++++ selftest/knownfail_mit_kdc
+@@ -581,7 +581,3 @@ samba.tests.krb5.as_canonicalization_tests.samba.tests.krb5.as_canonicalization_
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_no_canonicalize_realm_case.ad_dc
+ ^samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_ticket_requester_sid_tgs.ad_dc
+-#
+-# Kpasswd tests
+-#
+-samba.tests.krb5.kpasswd_tests.samba.tests.krb5.kpasswd_tests.KpasswdTests.test_kpasswd_tgt.ad_dc
+diff --git source4/kdc/kpasswd-helper.c source4/kdc/kpasswd-helper.c
+index 55a2f5b3bf6..2ffdb79aea5 100644
+--- source4/kdc/kpasswd-helper.c
++++ source4/kdc/kpasswd-helper.c
+@@ -241,3 +241,23 @@ NTSTATUS kpasswd_samdb_set_password(TALLOC_CTX *mem_ctx,
+
+ return status;
+ }
++
++krb5_error_code kpasswd_check_non_tgt(struct auth_session_info *session_info,
++ const char **error_string)
++{
++ switch(session_info->ticket_type) {
++ case TICKET_TYPE_TGT:
++ /* TGTs are disallowed here. */
++ *error_string = "A TGT may not be used as a ticket to kpasswd";
++ return KRB5_KPASSWD_AUTHERROR;
++ case TICKET_TYPE_NON_TGT:
++ /* Non-TGTs are permitted, and expected. */
++ break;
++ default:
++ /* In case we forgot to set the type. */
++ *error_string = "Failed to ascertain that ticket to kpasswd is not a TGT";
++ return KRB5_KPASSWD_HARDERROR;
++ }
++
++ return 0;
++}
+diff --git source4/kdc/kpasswd-helper.h source4/kdc/kpasswd-helper.h
+index 8fad81e0a5d..94a6e2acfdd 100644
+--- source4/kdc/kpasswd-helper.h
++++ source4/kdc/kpasswd-helper.h
+@@ -43,4 +43,6 @@ NTSTATUS kpasswd_samdb_set_password(TALLOC_CTX *mem_ctx,
+ enum samPwdChangeReason *reject_reason,
+ struct samr_DomInfo1 **dominfo);
+
++krb5_error_code kpasswd_check_non_tgt(struct auth_session_info *session_info,
++ const char **error_string);
+ #endif /* _KPASSWD_HELPER_H */
+diff --git source4/kdc/kpasswd-service-heimdal.c source4/kdc/kpasswd-service-heimdal.c
+index a0352d1ad35..4d009b9eb24 100644
+--- source4/kdc/kpasswd-service-heimdal.c
++++ source4/kdc/kpasswd-service-heimdal.c
+@@ -253,6 +253,7 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
+ {
+ struct auth_session_info *session_info;
+ NTSTATUS status;
++ krb5_error_code code;
+
+ status = gensec_session_info(gensec_security,
+ mem_ctx,
+@@ -264,6 +265,18 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
+ return KRB5_KPASSWD_HARDERROR;
+ }
+
++ /*
++ * Since the kpasswd service shares its keys with the krbtgt, we might
++ * have received a TGT rather than a kpasswd ticket. We need to check
++ * the ticket type to ensure that TGTs cannot be misused in this manner.
++ */
++ code = kpasswd_check_non_tgt(session_info,
++ error_string);
++ if (code != 0) {
++ DBG_WARNING("%s\n", *error_string);
++ return code;
++ }
++
+ switch(verno) {
+ case KRB5_KPASSWD_VERS_CHANGEPW: {
+ DATA_BLOB password = data_blob_null;
+diff --git source4/kdc/kpasswd-service-mit.c source4/kdc/kpasswd-service-mit.c
+index de4c6f3f622..6b051567b6e 100644
+--- source4/kdc/kpasswd-service-mit.c
++++ source4/kdc/kpasswd-service-mit.c
+@@ -332,6 +332,7 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
+ {
+ struct auth_session_info *session_info;
+ NTSTATUS status;
++ krb5_error_code code;
+
+ status = gensec_session_info(gensec_security,
+ mem_ctx,
+@@ -344,6 +345,18 @@ krb5_error_code kpasswd_handle_request(struct kdc_server *kdc,
+ return KRB5_KPASSWD_HARDERROR;
+ }
+
++ /*
++ * Since the kpasswd service shares its keys with the krbtgt, we might
++ * have received a TGT rather than a kpasswd ticket. We need to check
++ * the ticket type to ensure that TGTs cannot be misused in this manner.
++ */
++ code = kpasswd_check_non_tgt(session_info,
++ error_string);
++ if (code != 0) {
++ DBG_WARNING("%s\n", *error_string);
++ return code;
++ }
++
+ switch(verno) {
+ case 1: {
+ DATA_BLOB password;
+--
+2.25.1
+
+
+From a6231af1f1c03cd81614332f867916e1748e03a8 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton@catalyst.net.nz>
+Date: Thu, 23 Jun 2022 13:59:11 +1200
+Subject: [PATCH 97/99] CVE-2022-2031 testprogs: Add test for short-lived
+ ticket across an incoming trust
+
+We ensure that the KDC does not reject a TGS-REQ with our short-lived
+TGT over an incoming trust.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>
+
+[jsutton@samba.org Changed --use-krb5-ccache to -k yes to match
+ surrounding usage]
+---
+ testprogs/blackbox/test_kinit_trusts_heimdal.sh | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git testprogs/blackbox/test_kinit_trusts_heimdal.sh testprogs/blackbox/test_kinit_trusts_heimdal.sh
+index bf0b81a0473..621434eac35 100755
+--- testprogs/blackbox/test_kinit_trusts_heimdal.sh
++++ testprogs/blackbox/test_kinit_trusts_heimdal.sh
+@@ -54,6 +54,10 @@ testit "kinit with password" $samba4kinit $enctype --password-file=$PREFIX/tmppa
+ test_smbclient "Test login with user kerberos ccache" 'ls' "$unc" -k yes || failed=`expr $failed + 1`
+ rm -rf $KRB5CCNAME_PATH
+
++testit "kinit with password and two minute lifetime" $samba4kinit $enctype --password-file=$PREFIX/tmppassfile --request-pac --server=krbtgt/$REALM@$TRUST_REALM --lifetime=2m $TRUST_USERNAME@$TRUST_REALM || failed=`expr $failed + 1`
++test_smbclient "Test login with user kerberos ccache and two minute lifetime" 'ls' "$unc" -k yes || failed=`expr $failed + 1`
++rm -rf $KRB5CCNAME_PATH
++
+ # Test with smbclient4
+ smbclient="$samba4bindir/smbclient4"
+ testit "kinit with password" $samba4kinit $enctype --password-file=$PREFIX/tmppassfile --request-pac $TRUST_USERNAME@$TRUST_REALM || failed=`expr $failed + 1`
+@@ -94,5 +98,5 @@ testit "wbinfo check outgoing trust pw" $VALGRIND $wbinfo --check-secret --domai
+
+ test_smbclient "Test user login with the changed outgoing secret" 'ls' "$unc" -k yes -U$USERNAME@$REALM%$PASSWORD || failed=`expr $failed + 1`
+
+-rm -f $PREFIX/tmpccache tmpccfile tmppassfile tmpuserpassfile tmpuserccache
++rm -f $PREFIX/tmpccache $PREFIX/tmppassfile
+ exit $failed
+--
+2.25.1
+
+
+From f6e1750c4fc966c29c2e0663d3c04e87057fa0c3 Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra@samba.org>
+Date: Tue, 7 Jun 2022 09:40:45 -0700
+Subject: [PATCH 98/99] CVE-2022-32742: s4: torture: Add raw.write.bad-write
+ test.
+
+Reproduces the test code in:
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15085
+
+Add knownfail.
+
+Signed-off-by: Jeremy Allison <jra@samba.org>
+Reviewed-by: David Disseldorp <ddiss@samba.org>
+---
+ selftest/knownfail.d/bad-write | 2 +
+ source4/torture/raw/write.c | 89 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 91 insertions(+)
+ create mode 100644 selftest/knownfail.d/bad-write
+
+diff --git selftest/knownfail.d/bad-write selftest/knownfail.d/bad-write
+new file mode 100644
+index 00000000000..5fc16606a13
+--- /dev/null
++++ selftest/knownfail.d/bad-write
+@@ -0,0 +1,2 @@
++^samba3.raw.write.bad-write\(nt4_dc_smb1\)
++^samba3.raw.write.bad-write\(ad_dc_smb1\)
+diff --git source4/torture/raw/write.c source4/torture/raw/write.c
+index 0a2f50f425b..661485bb548 100644
+--- source4/torture/raw/write.c
++++ source4/torture/raw/write.c
+@@ -25,6 +25,7 @@
+ #include "libcli/libcli.h"
+ #include "torture/util.h"
+ #include "torture/raw/proto.h"
++#include "libcli/raw/raw_proto.h"
+
+ #define CHECK_STATUS(status, correct) do { \
+ if (!NT_STATUS_EQUAL(status, correct)) { \
+@@ -694,6 +695,93 @@ done:
+ return ret;
+ }
+
++/*
++ test a deliberately bad SMB1 write.
++*/
++static bool test_bad_write(struct torture_context *tctx,
++ struct smbcli_state *cli)
++{
++ bool ret = false;
++ int fnum = -1;
++ struct smbcli_request *req = NULL;
++ const char *fname = BASEDIR "\\badwrite.txt";
++ bool ok = false;
++
++ if (!torture_setup_dir(cli, BASEDIR)) {
++ torture_fail(tctx, "failed to setup basedir");
++ }
++
++ torture_comment(tctx, "Testing RAW_BAD_WRITE\n");
++
++ fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
++ if (fnum == -1) {
++ torture_fail_goto(tctx,
++ done,
++ talloc_asprintf(tctx,
++ "Failed to create %s - %s\n",
++ fname,
++ smbcli_errstr(cli->tree)));
++ }
++
++ req = smbcli_request_setup(cli->tree,
++ SMBwrite,
++ 5,
++ 0);
++ if (req == NULL) {
++ torture_fail_goto(tctx,
++ done,
++ talloc_asprintf(tctx, "talloc fail\n"));
++ }
++
++ SSVAL(req->out.vwv, VWV(0), fnum);
++ SSVAL(req->out.vwv, VWV(1), 65535); /* bad write length. */
++ SIVAL(req->out.vwv, VWV(2), 0); /* offset */
++ SSVAL(req->out.vwv, VWV(4), 0); /* remaining. */
++
++ if (!smbcli_request_send(req)) {
++ torture_fail_goto(tctx,
++ done,
++ talloc_asprintf(tctx, "Send failed\n"));
++ }
++
++ if (!smbcli_request_receive(req)) {
++ torture_fail_goto(tctx,
++ done,
++ talloc_asprintf(tctx, "Reveive failed\n"));
++ }
++
++ /*
++ * Check for expected error codes.
++ * ntvfs returns NT_STATUS_UNSUCCESSFUL.
++ */
++ ok = (NT_STATUS_EQUAL(req->status, NT_STATUS_INVALID_PARAMETER) ||
++ NT_STATUS_EQUAL(req->status, NT_STATUS_UNSUCCESSFUL));
++
++ if (!ok) {
++ torture_fail_goto(tctx,
++ done,
++ talloc_asprintf(tctx,
++ "Should have returned "
++ "NT_STATUS_INVALID_PARAMETER or "
++ "NT_STATUS_UNSUCCESSFUL "
++ "got %s\n",
++ nt_errstr(req->status)));
++ }
++
++ ret = true;
++
++done:
++ if (req != NULL) {
++ smbcli_request_destroy(req);
++ }
++ if (fnum != -1) {
++ smbcli_close(cli->tree, fnum);
++ }
++ smb_raw_exit(cli->session);
++ smbcli_deltree(cli->tree, BASEDIR);
++ return ret;
++}
++
+ /*
+ basic testing of write calls
+ */
+@@ -705,6 +793,7 @@ struct torture_suite *torture_raw_write(TALLOC_CTX *mem_ctx)
+ torture_suite_add_1smb_test(suite, "write unlock", test_writeunlock);
+ torture_suite_add_1smb_test(suite, "write close", test_writeclose);
+ torture_suite_add_1smb_test(suite, "writex", test_writex);
++ torture_suite_add_1smb_test(suite, "bad-write", test_bad_write);
+
+ return suite;
+ }
+--
+2.25.1
+
+
+From 7720e0acfd7ea6a2339f3e389aa8dcedd6174095 Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra@samba.org>
+Date: Wed, 8 Jun 2022 13:50:51 -0700
+Subject: [PATCH 99/99] CVE-2022-32742: s3: smbd: Harden the smbreq_bufrem()
+ macro.
+
+Fixes the raw.write.bad-write test.
+
+NB. We need the two (==0) changes in source3/smbd/reply.c
+as the gcc optimizer now knows that the return from
+smbreq_bufrem() can never be less than zero.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15085
+
+Remove knownfail.
+
+Signed-off-by: Jeremy Allison <jra@samba.org>
+Reviewed-by: David Disseldorp <ddiss@samba.org>
+---
+ selftest/knownfail.d/bad-write | 2 --
+ source3/include/smb_macros.h | 2 +-
+ source3/smbd/reply.c | 4 ++--
+ 3 files changed, 3 insertions(+), 5 deletions(-)
+ delete mode 100644 selftest/knownfail.d/bad-write
+
+diff --git selftest/knownfail.d/bad-write selftest/knownfail.d/bad-write
+deleted file mode 100644
+index 5fc16606a13..00000000000
+--- selftest/knownfail.d/bad-write
++++ /dev/null
+@@ -1,2 +0,0 @@
+-^samba3.raw.write.bad-write\(nt4_dc_smb1\)
+-^samba3.raw.write.bad-write\(ad_dc_smb1\)
+diff --git source3/include/smb_macros.h source3/include/smb_macros.h
+index def122727f0..de1322a503b 100644
+--- source3/include/smb_macros.h
++++ source3/include/smb_macros.h
+@@ -152,7 +152,7 @@
+
+ /* the remaining number of bytes in smb buffer 'buf' from pointer 'p'. */
+ #define smb_bufrem(buf, p) (smb_buflen(buf)-PTR_DIFF(p, smb_buf(buf)))
+-#define smbreq_bufrem(req, p) (req->buflen - PTR_DIFF(p, req->buf))
++#define smbreq_bufrem(req, p) ((req)->buflen < PTR_DIFF((p), (req)->buf) ? 0 : (req)->buflen - PTR_DIFF((p), (req)->buf))
+
+
+ /* Note that chain_size must be available as an extern int to this macro. */
+diff --git source3/smbd/reply.c source3/smbd/reply.c
+index f33326564f7..b5abe588910 100644
+--- source3/smbd/reply.c
++++ source3/smbd/reply.c
+@@ -342,7 +342,7 @@ size_t srvstr_get_path_req(TALLOC_CTX *mem_ctx, struct smb_request *req,
+ {
+ ssize_t bufrem = smbreq_bufrem(req, src);
+
+- if (bufrem < 0) {
++ if (bufrem == 0) {
+ *err = NT_STATUS_INVALID_PARAMETER;
+ return 0;
+ }
+@@ -380,7 +380,7 @@ size_t srvstr_pull_req_talloc(TALLOC_CTX *ctx, struct smb_request *req,
+ {
+ ssize_t bufrem = smbreq_bufrem(req, src);
+
+- if (bufrem < 0) {
++ if (bufrem == 0) {
+ return 0;
+ }
+
+--
+2.25.1
+
diff --git a/net/samba413/files/patch-waf-2.0.20 b/net/samba413/files/patch-waf-2.0.20
new file mode 100644
index 000000000000..3c40ea15f0ed
--- /dev/null
+++ b/net/samba413/files/patch-waf-2.0.20
@@ -0,0 +1,1663 @@
+From 5fc3a71d0f54b176d3cb2e399718d0468507e797 Mon Sep 17 00:00:00 2001
+From: David Mulder <dmulder@suse.com>
+Date: Mon, 24 Aug 2020 13:12:46 -0600
+Subject: [PATCH] waf: upgrade to 2.0.20
+
+This contain an important change:
+"Fix gccdeps.scan() returning nodes that no longer exist on disk."
+https://gitlab.com/ita1024/waf/-/merge_requests/2293
+
+Signed-off-by: David Mulder <dmulder@suse.com>
+Reviewed-by: Stefan Metzmacher <metze@samba.org>
+---
+ buildtools/bin/waf | 2 +-
+ buildtools/wafsamba/samba_utils.py | 2 +-
+ buildtools/wafsamba/samba_waf18.py | 3 +-
+ buildtools/wafsamba/wafsamba.py | 2 +-
+ third_party/waf/waflib/Configure.py | 25 +-
+ third_party/waf/waflib/Context.py | 18 +-
+ third_party/waf/waflib/Options.py | 31 +-
+ third_party/waf/waflib/Scripting.py | 6 +-
+ third_party/waf/waflib/Tools/c_aliases.py | 4 +-
+ third_party/waf/waflib/Tools/c_config.py | 22 +-
+ third_party/waf/waflib/Tools/c_tests.py | 15 +-
+ third_party/waf/waflib/Tools/compiler_c.py | 2 +-
+ third_party/waf/waflib/Tools/compiler_cxx.py | 2 +-
+ third_party/waf/waflib/Tools/fc.py | 4 +-
+ third_party/waf/waflib/Tools/irixcc.py | 14 +-
+ third_party/waf/waflib/Tools/javaw.py | 2 +-
+ third_party/waf/waflib/Tools/python.py | 2 +-
+ third_party/waf/waflib/Tools/qt5.py | 6 +-
+ third_party/waf/waflib/Utils.py | 2 +-
+ .../extras/clang_compilation_database.py | 172 ++++--
+ third_party/waf/waflib/extras/doxygen.py | 1 +
+ third_party/waf/waflib/extras/gccdeps.py | 15 +-
+ third_party/waf/waflib/extras/javatest.py | 135 ++++-
+ third_party/waf/waflib/extras/msvc_pdb.py | 46 ++
+ third_party/waf/waflib/extras/pytest.py | 17 +-
+ third_party/waf/waflib/extras/wafcache.py | 524 ++++++++++++++++++
+ 26 files changed, 942 insertions(+), 132 deletions(-)
+ create mode 100644 third_party/waf/waflib/extras/msvc_pdb.py
+ create mode 100644 third_party/waf/waflib/extras/wafcache.py
+
+diff --git buildtools/bin/waf buildtools/bin/waf
+index 11ce8e7480a..feabe25d131 100755
+--- buildtools/bin/waf
++++ buildtools/bin/waf
+@@ -32,7 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
+
+ import os, sys, inspect
+
+-VERSION="2.0.18"
++VERSION="2.0.20"
+ REVISION="x"
+ GIT="x"
+ INSTALL="x"
+diff --git buildtools/wafsamba/samba_utils.py buildtools/wafsamba/samba_utils.py
+index 4afee249d33..0587f525aff 100644
+--- buildtools/wafsamba/samba_utils.py
++++ buildtools/wafsamba/samba_utils.py
+@@ -459,7 +459,7 @@ def RECURSE(ctx, directory):
+ return
+ visited_dirs.add(key)
+ relpath = os.path.relpath(abspath, ctx.path.abspath())
+- if ctxclass in ['tmp', 'OptionsContext', 'ConfigurationContext', 'BuildContext']:
++ if ctxclass in ['tmp', 'OptionsContext', 'ConfigurationContext', 'BuildContext', 'ClangDbContext']:
+ return ctx.recurse(relpath)
+ if 'waflib.extras.compat15' in sys.modules:
+ return ctx.recurse(relpath)
+diff --git buildtools/wafsamba/samba_waf18.py buildtools/wafsamba/samba_waf18.py
+index c0bb6bfcf55..ecf3891f175 100644
+--- buildtools/wafsamba/samba_waf18.py
++++ buildtools/wafsamba/samba_waf18.py
+@@ -5,6 +5,7 @@ from waflib import Build, Configure, Node, Utils, Options, Logs, TaskGen
+ from waflib import ConfigSet
+ from waflib.TaskGen import feature, after
+ from waflib.Configure import conf, ConfigurationContext
++from waflib.extras import clang_compilation_database
+
+ from waflib.Tools.flex import decide_ext
+
+@@ -37,7 +38,7 @@ TaskGen.declare_chain(
+ )
+
+
+-for y in (Build.BuildContext, Build.CleanContext, Build.InstallContext, Build.UninstallContext, Build.ListContext):
++for y in (Build.BuildContext, Build.CleanContext, Build.InstallContext, Build.UninstallContext, Build.ListContext, clang_compilation_database.ClangDbContext):
+ class tmp(y):
+ variant = 'default'
+
+diff --git buildtools/wafsamba/wafsamba.py buildtools/wafsamba/wafsamba.py
+index 7827d374654..9f6ee4f5c7f 100644
+--- buildtools/wafsamba/wafsamba.py
++++ buildtools/wafsamba/wafsamba.py
+@@ -38,7 +38,7 @@ LIB_PATH="shared"
+
+ os.environ['PYTHONUNBUFFERED'] = '1'
+
+-if Context.HEXVERSION not in (0x2001200,):
++if Context.HEXVERSION not in (0x2001400,):
+ Logs.error('''
+ Please use the version of waf that comes with Samba, not
+ a system installed version. See http://wiki.samba.org/index.php/Waf
+diff --git third_party/waf/waflib/Configure.py third_party/waf/waflib/Configure.py
+index 5762eb66954..e7333948489 100644
+--- third_party/waf/waflib/Configure.py
++++ third_party/waf/waflib/Configure.py
+@@ -508,23 +508,27 @@ def find_binary(self, filenames, exts, paths):
+ @conf
+ def run_build(self, *k, **kw):
+ """
+- Create a temporary build context to execute a build. A reference to that build
+- context is kept on self.test_bld for debugging purposes, and you should not rely
+- on it too much (read the note on the cache below).
+- The parameters given in the arguments to this function are passed as arguments for
+- a single task generator created in the build. Only three parameters are obligatory:
++ Create a temporary build context to execute a build. A temporary reference to that build
++ context is kept on self.test_bld for debugging purposes.
++ The arguments to this function are passed to a single task generator for that build.
++ Only three parameters are mandatory:
+
+ :param features: features to pass to a task generator created in the build
+ :type features: list of string
+ :param compile_filename: file to create for the compilation (default: *test.c*)
+ :type compile_filename: string
+- :param code: code to write in the filename to compile
++ :param code: input file contents
+ :type code: string
+
+- Though this function returns *0* by default, the build may set an attribute named *retval* on the
++ Though this function returns *0* by default, the build may bind attribute named *retval* on the
+ build context object to return a particular value. See :py:func:`waflib.Tools.c_config.test_exec_fun` for example.
+
+- This function also features a cache which can be enabled by the following option::
++ The temporary builds creates a temporary folder; the name of that folder is calculated
++ by hashing input arguments to this function, with the exception of :py:class:`waflib.ConfigSet.ConfigSet`
++ objects which are used for both reading and writing values.
++
++ This function also features a cache which is disabled by default; that cache relies
++ on the hash value calculated as indicated above::
+
+ def options(opt):
+ opt.add_option('--confcache', dest='confcache', default=0,
+@@ -538,7 +542,10 @@ def run_build(self, *k, **kw):
+ buf = []
+ for key in sorted(kw.keys()):
+ v = kw[key]
+- if hasattr(v, '__call__'):
++ if isinstance(v, ConfigSet.ConfigSet):
++ # values are being written to, so they are excluded from contributing to the hash
++ continue
++ elif hasattr(v, '__call__'):
+ buf.append(Utils.h_fun(v))
+ else:
+ buf.append(str(v))
+diff --git third_party/waf/waflib/Context.py third_party/waf/waflib/Context.py
+index e3305fa3341..3f1b4fa48ab 100644
+--- third_party/waf/waflib/Context.py
++++ third_party/waf/waflib/Context.py
+@@ -6,20 +6,30 @@
+ Classes and functions enabling the command system
+ """
+
+-import os, re, imp, sys
++import os, re, sys
+ from waflib import Utils, Errors, Logs
+ import waflib.Node
+
++if sys.hexversion > 0x3040000:
++ import types
++ class imp(object):
++ new_module = lambda x: types.ModuleType(x)
++else:
++ import imp
++
+ # the following 3 constants are updated on each new release (do not touch)
+-HEXVERSION=0x2001200
++HEXVERSION=0x2001400
+ """Constant updated on new releases"""
+
+-WAFVERSION="2.0.18"
++WAFVERSION="2.0.20"
+ """Constant updated on new releases"""
+
+-WAFREVISION="314689b8994259a84f0de0aaef74d7ce91f541ad"
++WAFREVISION="668769470956da8c5b60817cb8884cd7d0f87cd4"
+ """Git revision when the waf version is updated"""
+
++WAFNAME="waf"
++"""Application name displayed on --help"""
++
+ ABI = 20
+ """Version of the build data cache file format (used in :py:const:`waflib.Context.DBFILE`)"""
+
+diff --git third_party/waf/waflib/Options.py third_party/waf/waflib/Options.py
+index ad802d4b905..d4104917c82 100644
+--- third_party/waf/waflib/Options.py
++++ third_party/waf/waflib/Options.py
+@@ -44,7 +44,7 @@ class opt_parser(optparse.OptionParser):
+ """
+ def __init__(self, ctx, allow_unknown=False):
+ optparse.OptionParser.__init__(self, conflict_handler='resolve', add_help_option=False,
+- version='waf %s (%s)' % (Context.WAFVERSION, Context.WAFREVISION))
++ version='%s %s (%s)' % (Context.WAFNAME, Context.WAFVERSION, Context.WAFREVISION))
+ self.formatter.width = Logs.get_term_cols()
+ self.ctx = ctx
+ self.allow_unknown = allow_unknown
+@@ -62,6 +62,21 @@ class opt_parser(optparse.OptionParser):
+ else:
+ self.error(str(e))
+
++ def _process_long_opt(self, rargs, values):
++ # --custom-option=-ftxyz is interpreted as -f -t... see #2280
++ if self.allow_unknown:
++ back = [] + rargs
++ try:
++ optparse.OptionParser._process_long_opt(self, rargs, values)
++ except optparse.BadOptionError:
++ while rargs:
++ rargs.pop()
++ rargs.extend(back)
++ rargs.pop(0)
++ raise
++ else:
++ optparse.OptionParser._process_long_opt(self, rargs, values)
++
+ def print_usage(self, file=None):
+ return self.print_help(file)
+
+@@ -96,11 +111,11 @@ class opt_parser(optparse.OptionParser):
+ lst.sort()
+ ret = '\n'.join(lst)
+
+- return '''waf [commands] [options]
++ return '''%s [commands] [options]
+
+-Main commands (example: ./waf build -j4)
++Main commands (example: ./%s build -j4)
+ %s
+-''' % ret
++''' % (Context.WAFNAME, Context.WAFNAME, ret)
+
+
+ class OptionsContext(Context.Context):
+@@ -141,9 +156,9 @@ class OptionsContext(Context.Context):
+ gr.add_option('-o', '--out', action='store', default='', help='build dir for the project', dest='out')
+ gr.add_option('-t', '--top', action='store', default='', help='src dir for the project', dest='top')
+
+- gr.add_option('--no-lock-in-run', action='store_true', default='', help=optparse.SUPPRESS_HELP, dest='no_lock_in_run')
+- gr.add_option('--no-lock-in-out', action='store_true', default='', help=optparse.SUPPRESS_HELP, dest='no_lock_in_out')
+- gr.add_option('--no-lock-in-top', action='store_true', default='', help=optparse.SUPPRESS_HELP, dest='no_lock_in_top')
++ gr.add_option('--no-lock-in-run', action='store_true', default=os.environ.get('NO_LOCK_IN_RUN', ''), help=optparse.SUPPRESS_HELP, dest='no_lock_in_run')
++ gr.add_option('--no-lock-in-out', action='store_true', default=os.environ.get('NO_LOCK_IN_OUT', ''), help=optparse.SUPPRESS_HELP, dest='no_lock_in_out')
++ gr.add_option('--no-lock-in-top', action='store_true', default=os.environ.get('NO_LOCK_IN_TOP', ''), help=optparse.SUPPRESS_HELP, dest='no_lock_in_top')
+
+ default_prefix = getattr(Context.g_module, 'default_prefix', os.environ.get('PREFIX'))
+ if not default_prefix:
+@@ -282,6 +297,8 @@ class OptionsContext(Context.Context):
+ elif arg != 'options':
+ commands.append(arg)
+
++ if options.jobs < 1:
++ options.jobs = 1
+ for name in 'top out destdir prefix bindir libdir'.split():
+ # those paths are usually expanded from Context.launch_dir
+ if getattr(options, name, None):
+diff --git third_party/waf/waflib/Scripting.py third_party/waf/waflib/Scripting.py
+index 68dccf29ce0..da83a2166a1 100644
+--- third_party/waf/waflib/Scripting.py
++++ third_party/waf/waflib/Scripting.py
+@@ -306,7 +306,7 @@ def distclean(ctx):
+
+ # remove a build folder, if any
+ cur = '.'
+- if ctx.options.no_lock_in_top:
++ if os.environ.get('NO_LOCK_IN_TOP') or ctx.options.no_lock_in_top:
+ cur = ctx.options.out
+
+ try:
+@@ -333,9 +333,9 @@ def distclean(ctx):
+ remove_and_log(env.out_dir, shutil.rmtree)
+
+ env_dirs = [env.out_dir]
+- if not ctx.options.no_lock_in_top:
++ if not (os.environ.get('NO_LOCK_IN_TOP') or ctx.options.no_lock_in_top):
+ env_dirs.append(env.top_dir)
+- if not ctx.options.no_lock_in_run:
++ if not (os.environ.get('NO_LOCK_IN_RUN') or ctx.options.no_lock_in_run):
+ env_dirs.append(env.run_dir)
+ for k in env_dirs:
+ p = os.path.join(k, Options.lockfile)
+diff --git third_party/waf/waflib/Tools/c_aliases.py third_party/waf/waflib/Tools/c_aliases.py
+index 985e048bdb7..928cfe29caa 100644
+--- third_party/waf/waflib/Tools/c_aliases.py
++++ third_party/waf/waflib/Tools/c_aliases.py
+@@ -38,7 +38,7 @@ def sniff_features(**kw):
+ :return: the list of features for a task generator processing the source files
+ :rtype: list of string
+ """
+- exts = get_extensions(kw['source'])
++ exts = get_extensions(kw.get('source', []))
+ typ = kw['typ']
+ feats = []
+
+@@ -72,7 +72,7 @@ def sniff_features(**kw):
+ feats.append(x + typ)
+ will_link = True
+ if not will_link and not kw.get('features', []):
+- raise Errors.WafError('Cannot link from %r, try passing eg: features="c cprogram"?' % kw)
++ raise Errors.WafError('Unable to determine how to link %r, try adding eg: features="c cshlib"?' % kw)
+ return feats
+
+ def set_features(kw, typ):
+diff --git third_party/waf/waflib/Tools/c_config.py third_party/waf/waflib/Tools/c_config.py
+index 80580cc9fcb..98187fac2e2 100644
+--- third_party/waf/waflib/Tools/c_config.py
++++ third_party/waf/waflib/Tools/c_config.py
+@@ -86,6 +86,10 @@ def parse_flags(self, line, uselib_store, env=None, force_static=False, posix=No
+ :type uselib_store: string
+ :param env: config set or conf.env by default
+ :type env: :py:class:`waflib.ConfigSet.ConfigSet`
++ :param force_static: force usage of static libraries
++ :type force_static: bool default False
++ :param posix: usage of POSIX mode for shlex lexical analiysis library
++ :type posix: bool default True
+ """
+
+ assert(isinstance(line, str))
+@@ -103,6 +107,8 @@ def parse_flags(self, line, uselib_store, env=None, force_static=False, posix=No
+ lex.commenters = ''
+ lst = list(lex)
+
++ so_re = re.compile(r"\.so(?:\.[0-9]+)*$")
++
+ # append_unique is not always possible
+ # for example, apple flags may require both -arch i386 and -arch ppc
+ uselib = uselib_store
+@@ -144,7 +150,7 @@ def parse_flags(self, line, uselib_store, env=None, force_static=False, posix=No
+ elif x.startswith('-std='):
+ prefix = 'CXXFLAGS' if '++' in x else 'CFLAGS'
+ app(prefix, x)
+- elif x.startswith('+') or x in ('-pthread', '-fPIC', '-fpic', '-fPIE', '-fpie'):
++ elif x.startswith('+') or x in ('-pthread', '-fPIC', '-fpic', '-fPIE', '-fpie', '-flto', '-fno-lto'):
+ app('CFLAGS', x)
+ app('CXXFLAGS', x)
+ app('LINKFLAGS', x)
+@@ -180,7 +186,7 @@ def parse_flags(self, line, uselib_store, env=None, force_static=False, posix=No
+ app('CFLAGS', tmp)
+ app('CXXFLAGS', tmp)
+ app('LINKFLAGS', tmp)
+- elif x.endswith(('.a', '.so', '.dylib', '.lib')):
++ elif x.endswith(('.a', '.dylib', '.lib')) or so_re.search(x):
+ appu('LINKFLAGS', x) # not cool, #762
+ else:
+ self.to_log('Unhandled flag %r' % x)
+@@ -246,6 +252,8 @@ def exec_cfg(self, kw):
+ * if modversion is given, then return the module version
+ * else, execute the *-config* program with the *args* and *variables* given, and set the flags on the *conf.env.FLAGS_name* variable
+
++ :param path: the **-config program to use**
++ :type path: list of string
+ :param atleast_pkgconfig_version: minimum pkg-config version to use (disable other tests)
+ :type atleast_pkgconfig_version: string
+ :param package: package name, for example *gtk+-2.0*
+@@ -260,6 +268,12 @@ def exec_cfg(self, kw):
+ :type variables: list of string
+ :param define_variable: additional variables to define (also in conf.env.PKG_CONFIG_DEFINES)
+ :type define_variable: dict(string: string)
++ :param pkg_config_path: paths where pkg-config should search for .pc config files (overrides env.PKG_CONFIG_PATH if exists)
++ :type pkg_config_path: string, list of directories separated by colon
++ :param force_static: force usage of static libraries
++ :type force_static: bool default False
++ :param posix: usage of POSIX mode for shlex lexical analiysis library
++ :type posix: bool default True
+ """
+
+ path = Utils.to_list(kw['path'])
+@@ -334,6 +348,7 @@ def check_cfg(self, *k, **kw):
+ """
+ Checks for configuration flags using a **-config**-like program (pkg-config, sdl-config, etc).
+ This wraps internal calls to :py:func:`waflib.Tools.c_config.validate_cfg` and :py:func:`waflib.Tools.c_config.exec_cfg`
++ so check exec_cfg parameters descriptions for more details on kw passed
+
+ A few examples::
+
+@@ -1267,10 +1282,11 @@ def multicheck(self, *k, **kw):
+ tasks = []
+
+ id_to_task = {}
+- for dct in k:
++ for counter, dct in enumerate(k):
+ x = Task.classes['cfgtask'](bld=bld, env=None)
+ tasks.append(x)
+ x.args = dct
++ x.args['multicheck_counter'] = counter
+ x.bld = bld
+ x.conf = self
+ x.args = dct
+diff --git third_party/waf/waflib/Tools/c_tests.py third_party/waf/waflib/Tools/c_tests.py
+index 7a4094f2450..bdd186c6bc4 100644
+--- third_party/waf/waflib/Tools/c_tests.py
++++ third_party/waf/waflib/Tools/c_tests.py
+@@ -180,9 +180,15 @@ def check_large_file(self, **kw):
+ ########################################################################################
+
+ ENDIAN_FRAGMENT = '''
++#ifdef _MSC_VER
++#define testshlib_EXPORT __declspec(dllexport)
++#else
++#define testshlib_EXPORT
++#endif
++
+ short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
+ short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
+-int use_ascii (int i) {
++int testshlib_EXPORT use_ascii (int i) {
+ return ascii_mm[i] + ascii_ii[i];
+ }
+ short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
+@@ -208,12 +214,12 @@ class grep_for_endianness(Task.Task):
+ return -1
+
+ @feature('grep_for_endianness')
+-@after_method('process_source')
++@after_method('apply_link')
+ def grep_for_endianness_fun(self):
+ """
+ Used by the endianness configuration test
+ """
+- self.create_task('grep_for_endianness', self.compiled_tasks[0].outputs[0])
++ self.create_task('grep_for_endianness', self.link_task.outputs[0])
+
+ @conf
+ def check_endianness(self):
+@@ -223,7 +229,8 @@ def check_endianness(self):
+ tmp = []
+ def check_msg(self):
+ return tmp[0]
+- self.check(fragment=ENDIAN_FRAGMENT, features='c grep_for_endianness',
++
++ self.check(fragment=ENDIAN_FRAGMENT, features='c cshlib grep_for_endianness',
+ msg='Checking for endianness', define='ENDIANNESS', tmp=tmp,
+ okmsg=check_msg, confcache=None)
+ return tmp[0]
+diff --git third_party/waf/waflib/Tools/compiler_c.py third_party/waf/waflib/Tools/compiler_c.py
+index 2dba3f82704..931dc57efec 100644
+--- third_party/waf/waflib/Tools/compiler_c.py
++++ third_party/waf/waflib/Tools/compiler_c.py
+@@ -37,7 +37,7 @@ from waflib.Logs import debug
+
+ c_compiler = {
+ 'win32': ['msvc', 'gcc', 'clang'],
+-'cygwin': ['gcc'],
++'cygwin': ['gcc', 'clang'],
+ 'darwin': ['clang', 'gcc'],
+ 'aix': ['xlc', 'gcc', 'clang'],
+ 'linux': ['gcc', 'clang', 'icc'],
+diff --git third_party/waf/waflib/Tools/compiler_cxx.py third_party/waf/waflib/Tools/compiler_cxx.py
+index 1af65a226dc..09fca7e4dc6 100644
+--- third_party/waf/waflib/Tools/compiler_cxx.py
++++ third_party/waf/waflib/Tools/compiler_cxx.py
+@@ -38,7 +38,7 @@ from waflib.Logs import debug
+
+ cxx_compiler = {
+ 'win32': ['msvc', 'g++', 'clang++'],
+-'cygwin': ['g++'],
++'cygwin': ['g++', 'clang++'],
+ 'darwin': ['clang++', 'g++'],
+ 'aix': ['xlc++', 'g++', 'clang++'],
+ 'linux': ['g++', 'clang++', 'icpc'],
+diff --git third_party/waf/waflib/Tools/fc.py third_party/waf/waflib/Tools/fc.py
+index fd4d39c90ae..7fbd76d3650 100644
+--- third_party/waf/waflib/Tools/fc.py
++++ third_party/waf/waflib/Tools/fc.py
+@@ -13,8 +13,8 @@ from waflib.TaskGen import extension
+ from waflib.Configure import conf
+
+ ccroot.USELIB_VARS['fc'] = set(['FCFLAGS', 'DEFINES', 'INCLUDES', 'FCPPFLAGS'])
+-ccroot.USELIB_VARS['fcprogram_test'] = ccroot.USELIB_VARS['fcprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
+-ccroot.USELIB_VARS['fcshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
++ccroot.USELIB_VARS['fcprogram_test'] = ccroot.USELIB_VARS['fcprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'LDFLAGS'])
++ccroot.USELIB_VARS['fcshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'LDFLAGS'])
+ ccroot.USELIB_VARS['fcstlib'] = set(['ARFLAGS', 'LINKDEPS'])
+
+ @extension('.f','.F','.f90','.F90','.for','.FOR','.f95','.F95','.f03','.F03','.f08','.F08')
+diff --git third_party/waf/waflib/Tools/irixcc.py third_party/waf/waflib/Tools/irixcc.py
+index c3ae1ac915c..0335c13cb61 100644
+--- third_party/waf/waflib/Tools/irixcc.py
++++ third_party/waf/waflib/Tools/irixcc.py
+@@ -13,22 +13,11 @@ from waflib.Configure import conf
+ @conf
+ def find_irixcc(conf):
+ v = conf.env
+- cc = None
+- if v.CC:
+- cc = v.CC
+- elif 'CC' in conf.environ:
+- cc = conf.environ['CC']
+- if not cc:
+- cc = conf.find_program('cc', var='CC')
+- if not cc:
+- conf.fatal('irixcc was not found')
+-
++ cc = conf.find_program('cc', var='CC')
+ try:
+ conf.cmd_and_log(cc + ['-version'])
+ except Errors.WafError:
+ conf.fatal('%r -version could not be executed' % cc)
+-
+- v.CC = cc
+ v.CC_NAME = 'irix'
+
+ @conf
+@@ -57,7 +46,6 @@ def irixcc_common_flags(conf):
+
+ def configure(conf):
+ conf.find_irixcc()
+- conf.find_cpp()
+ conf.find_ar()
+ conf.irixcc_common_flags()
+ conf.cc_load_tools()
+diff --git third_party/waf/waflib/Tools/javaw.py third_party/waf/waflib/Tools/javaw.py
+index ceb08c28c87..b7f5dd1f87f 100644
+--- third_party/waf/waflib/Tools/javaw.py
++++ third_party/waf/waflib/Tools/javaw.py
+@@ -251,7 +251,7 @@ def use_javac_files(self):
+ base_node = tg.path.get_bld()
+
+ self.use_lst.append(base_node.abspath())
+- self.javac_task.dep_nodes.extend([x for x in base_node.ant_glob(JAR_RE, remove=False, quiet=True)])
++ self.javac_task.dep_nodes.extend([dx for dx in base_node.ant_glob(JAR_RE, remove=False, quiet=True)])
+
+ for tsk in tg.tasks:
+ self.javac_task.set_run_after(tsk)
+diff --git third_party/waf/waflib/Tools/python.py third_party/waf/waflib/Tools/python.py
+index 7c45a76ffd2..b1c8dd01285 100644
+--- third_party/waf/waflib/Tools/python.py
++++ third_party/waf/waflib/Tools/python.py
+@@ -620,7 +620,7 @@ def configure(conf):
+ v.PYO = getattr(Options.options, 'pyo', 1)
+
+ try:
+- v.PYTAG = conf.cmd_and_log(conf.env.PYTHON + ['-c', "import imp;print(imp.get_tag())"]).strip()
++ v.PYTAG = conf.cmd_and_log(conf.env.PYTHON + ['-c', "import sys\ntry:\n print(sys.implementation.cache_tag)\nexcept AttributeError:\n import imp\n print(imp.get_tag())\n"]).strip()
+ except Errors.WafError:
+ pass
+
+diff --git third_party/waf/waflib/Tools/qt5.py third_party/waf/waflib/Tools/qt5.py
+index 287c25374a4..99e021bae61 100644
+--- third_party/waf/waflib/Tools/qt5.py
++++ third_party/waf/waflib/Tools/qt5.py
+@@ -482,8 +482,8 @@ def configure(self):
+ self.fatal('No CXX compiler defined: did you forget to configure compiler_cxx first?')
+
+ # Qt5 may be compiled with '-reduce-relocations' which requires dependent programs to have -fPIE or -fPIC?
+- frag = '#include <QApplication>\nint main(int argc, char **argv) {return 0;}\n'
+- uses = 'QT5CORE QT5WIDGETS QT5GUI'
++ frag = '#include <QMap>\nint main(int argc, char **argv) {QMap<int,int> m;return m.keys().size();}\n'
++ uses = 'QT5CORE'
+ for flag in [[], '-fPIE', '-fPIC', '-std=c++11' , ['-std=c++11', '-fPIE'], ['-std=c++11', '-fPIC']]:
+ msg = 'See if Qt files compile '
+ if flag:
+@@ -499,7 +499,7 @@ def configure(self):
+
+ # FreeBSD does not add /usr/local/lib and the pkg-config files do not provide it either :-/
+ if Utils.unversioned_sys_platform() == 'freebsd':
+- frag = '#include <QApplication>\nint main(int argc, char **argv) { QApplication app(argc, argv); return NULL != (void*) (&app);}\n'
++ frag = '#include <QMap>\nint main(int argc, char **argv) {QMap<int,int> m;return m.keys().size();}\n'
+ try:
+ self.check(features='qt5 cxx cxxprogram', use=uses, fragment=frag, msg='Can we link Qt programs on FreeBSD directly?')
+ except self.errors.ConfigurationError:
+diff --git third_party/waf/waflib/Utils.py third_party/waf/waflib/Utils.py
+index 7472226da58..fc64fa05154 100644
+--- third_party/waf/waflib/Utils.py
++++ third_party/waf/waflib/Utils.py
+@@ -891,7 +891,7 @@ def run_prefork_process(cmd, kwargs, cargs):
+ """
+ Delegates process execution to a pre-forked process instance.
+ """
+- if not 'env' in kwargs:
++ if not kwargs.get('env'):
+ kwargs['env'] = dict(os.environ)
+ try:
+ obj = base64.b64encode(cPickle.dumps([cmd, kwargs, cargs]))
+diff --git third_party/waf/waflib/extras/clang_compilation_database.py third_party/waf/waflib/extras/clang_compilation_database.py
+index 4d9b5e275ae..ff71f22ecfd 100644
+--- third_party/waf/waflib/extras/clang_compilation_database.py
++++ third_party/waf/waflib/extras/clang_compilation_database.py
+@@ -1,6 +1,7 @@
+ #!/usr/bin/env python
+ # encoding: utf-8
+ # Christoph Koke, 2013
++# Alibek Omarov, 2019
+
+ """
+ Writes the c and cpp compile commands into build/compile_commands.json
+@@ -8,14 +9,23 @@ see http://clang.llvm.org/docs/JSONCompilationDatabase.html
+
+ Usage:
+
+- def configure(conf):
+- conf.load('compiler_cxx')
+- ...
+- conf.load('clang_compilation_database')
++ Load this tool in `options` to be able to generate database
++ by request in command-line and before build:
++
++ $ waf clangdb
++
++ def options(opt):
++ opt.load('clang_compilation_database')
++
++ Otherwise, load only in `configure` to generate it always before build.
++
++ def configure(conf):
++ conf.load('compiler_cxx')
++ ...
++ conf.load('clang_compilation_database')
+ """
+
+-import sys, os, json, shlex, pipes
+-from waflib import Logs, TaskGen, Task
++from waflib import Logs, TaskGen, Task, Build, Scripting
+
+ Task.Task.keep_last_cmd = True
+
+@@ -23,63 +33,103 @@ Task.Task.keep_last_cmd = True
+ @TaskGen.after_method('process_use')
+ def collect_compilation_db_tasks(self):
+ "Add a compilation database entry for compiled tasks"
+- try:
+- clang_db = self.bld.clang_compilation_database_tasks
+- except AttributeError:
+- clang_db = self.bld.clang_compilation_database_tasks = []
+- self.bld.add_post_fun(write_compilation_database)
++ if not isinstance(self.bld, ClangDbContext):
++ return
+
+ tup = tuple(y for y in [Task.classes.get(x) for x in ('c', 'cxx')] if y)
+ for task in getattr(self, 'compiled_tasks', []):
+ if isinstance(task, tup):
+- clang_db.append(task)
+-
+-def write_compilation_database(ctx):
+- "Write the clang compilation database as JSON"
+- database_file = ctx.bldnode.make_node('compile_commands.json')
+- Logs.info('Build commands will be stored in %s', database_file.path_from(ctx.path))
+- try:
+- root = json.load(database_file)
+- except IOError:
+- root = []
+- clang_db = dict((x['file'], x) for x in root)
+- for task in getattr(ctx, 'clang_compilation_database_tasks', []):
++ self.bld.clang_compilation_database_tasks.append(task)
++
++class ClangDbContext(Build.BuildContext):
++ '''generates compile_commands.json by request'''
++ cmd = 'clangdb'
++ clang_compilation_database_tasks = []
++
++ def write_compilation_database(self):
++ """
++ Write the clang compilation database as JSON
++ """
++ database_file = self.bldnode.make_node('compile_commands.json')
++ Logs.info('Build commands will be stored in %s', database_file.path_from(self.path))
+ try:
+- cmd = task.last_cmd
+- except AttributeError:
+- continue
+- directory = getattr(task, 'cwd', ctx.variant_dir)
+- f_node = task.inputs[0]
+- filename = os.path.relpath(f_node.abspath(), directory)
+- entry = {
+- "directory": directory,
+- "arguments": cmd,
+- "file": filename,
+- }
+- clang_db[filename] = entry
+- root = list(clang_db.values())
+- database_file.write(json.dumps(root, indent=2))
+-
+-# Override the runnable_status function to do a dummy/dry run when the file doesn't need to be compiled.
+-# This will make sure compile_commands.json is always fully up to date.
+-# Previously you could end up with a partial compile_commands.json if the build failed.
+-for x in ('c', 'cxx'):
+- if x not in Task.classes:
+- continue
+-
+- t = Task.classes[x]
+-
+- def runnable_status(self):
+- def exec_command(cmd, **kw):
+- pass
+-
+- run_status = self.old_runnable_status()
+- if run_status == Task.SKIP_ME:
+- setattr(self, 'old_exec_command', getattr(self, 'exec_command', None))
+- setattr(self, 'exec_command', exec_command)
+- self.run()
+- setattr(self, 'exec_command', getattr(self, 'old_exec_command', None))
+- return run_status
+-
+- setattr(t, 'old_runnable_status', getattr(t, 'runnable_status', None))
+- setattr(t, 'runnable_status', runnable_status)
++ root = database_file.read_json()
++ except IOError:
++ root = []
++ clang_db = dict((x['file'], x) for x in root)
++ for task in self.clang_compilation_database_tasks:
++ try:
++ cmd = task.last_cmd
++ except AttributeError:
++ continue
++ f_node = task.inputs[0]
++ filename = f_node.path_from(task.get_cwd())
++ entry = {
++ "directory": task.get_cwd().abspath(),
++ "arguments": cmd,
++ "file": filename,
++ }
++ clang_db[filename] = entry
++ root = list(clang_db.values())
++ database_file.write_json(root)
++
++ def execute(self):
++ """
++ Build dry run
++ """
++ self.restore()
++
++ if not self.all_envs:
++ self.load_envs()
++
++ self.recurse([self.run_dir])
++ self.pre_build()
++
++ # we need only to generate last_cmd, so override
++ # exec_command temporarily
++ def exec_command(self, *k, **kw):
++ return 0
++
++ for g in self.groups:
++ for tg in g:
++ try:
++ f = tg.post
++ except AttributeError:
++ pass
++ else:
++ f()
++
++ if isinstance(tg, Task.Task):
++ lst = [tg]
++ else: lst = tg.tasks
++ for tsk in lst:
++ tup = tuple(y for y in [Task.classes.get(x) for x in ('c', 'cxx')] if y)
++ if isinstance(tsk, tup):
++ old_exec = tsk.exec_command
++ tsk.exec_command = exec_command
++ tsk.run()
++ tsk.exec_command = old_exec
++
++ self.write_compilation_database()
++
++EXECUTE_PATCHED = False
++def patch_execute():
++ global EXECUTE_PATCHED
++
++ if EXECUTE_PATCHED:
++ return
++
++ def new_execute_build(self):
++ """
++ Invoke clangdb command before build
++ """
++ if self.cmd.startswith('build'):
++ Scripting.run_command('clangdb')
++
++ old_execute_build(self)
++
++ old_execute_build = getattr(Build.BuildContext, 'execute_build', None)
++ setattr(Build.BuildContext, 'execute_build', new_execute_build)
++ EXECUTE_PATCHED = True
++
++patch_execute()
+diff --git third_party/waf/waflib/extras/doxygen.py third_party/waf/waflib/extras/doxygen.py
+index 20cd9e1a852..de75bc2738a 100644
+--- third_party/waf/waflib/extras/doxygen.py
++++ third_party/waf/waflib/extras/doxygen.py
+@@ -69,6 +69,7 @@ def parse_doxy(txt):
+ class doxygen(Task.Task):
+ vars = ['DOXYGEN', 'DOXYFLAGS']
+ color = 'BLUE'
++ ext_in = [ '.py', '.c', '.h', '.java', '.pb.cc' ]
+
+ def runnable_status(self):
+ '''
+diff --git third_party/waf/waflib/extras/gccdeps.py third_party/waf/waflib/extras/gccdeps.py
+index bfabe72e6fd..c3a809e252a 100644
+--- third_party/waf/waflib/extras/gccdeps.py
++++ third_party/waf/waflib/extras/gccdeps.py
+@@ -27,7 +27,7 @@ if not c_preproc.go_absolute:
+ gccdeps_flags = ['-MMD']
+
+ # Third-party tools are allowed to add extra names in here with append()
+-supported_compilers = ['gcc', 'icc', 'clang']
++supported_compilers = ['gas', 'gcc', 'icc', 'clang']
+
+ def scan(self):
+ if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
+@@ -175,14 +175,14 @@ def wrap_compiled_task(classname):
+ derived_class.scan = scan
+ derived_class.sig_implicit_deps = sig_implicit_deps
+
+-for k in ('c', 'cxx'):
++for k in ('asm', 'c', 'cxx'):
+ if k in Task.classes:
+ wrap_compiled_task(k)
+
+ @before_method('process_source')
+ @feature('force_gccdeps')
+ def force_gccdeps(self):
+- self.env.ENABLE_GCCDEPS = ['c', 'cxx']
++ self.env.ENABLE_GCCDEPS = ['asm', 'c', 'cxx']
+
+ def configure(conf):
+ # in case someone provides a --enable-gccdeps command-line option
+@@ -191,6 +191,15 @@ def configure(conf):
+
+ global gccdeps_flags
+ flags = conf.env.GCCDEPS_FLAGS or gccdeps_flags
++ if conf.env.ASM_NAME in supported_compilers:
++ try:
++ conf.check(fragment='', features='asm force_gccdeps', asflags=flags, compile_filename='test.S', msg='Checking for asm flags %r' % ''.join(flags))
++ except Errors.ConfigurationError:
++ pass
++ else:
++ conf.env.append_value('ASFLAGS', flags)
++ conf.env.append_unique('ENABLE_GCCDEPS', 'asm')
++
+ if conf.env.CC_NAME in supported_compilers:
+ try:
+ conf.check(fragment='int main() { return 0; }', features='c force_gccdeps', cflags=flags, msg='Checking for c flags %r' % ''.join(flags))
+diff --git third_party/waf/waflib/extras/javatest.py third_party/waf/waflib/extras/javatest.py
+index 979b8d8242d..76d40edf250 100755
+--- third_party/waf/waflib/extras/javatest.py
++++ third_party/waf/waflib/extras/javatest.py
+@@ -1,6 +1,6 @@
+ #! /usr/bin/env python
+ # encoding: utf-8
+-# Federico Pellegrin, 2017 (fedepell)
++# Federico Pellegrin, 2019 (fedepell)
+
+ """
+ Provides Java Unit test support using :py:class:`waflib.Tools.waf_unit_test.utest`
+@@ -11,6 +11,10 @@ standard waf unit test environment. It has been tested with TestNG and JUnit
+ but should be easily expandable to other frameworks given the flexibility of
+ ut_str provided by the standard waf unit test environment.
+
++The extra takes care also of managing non-java dependencies (ie. C/C++ libraries
++using JNI or Python modules via JEP) and setting up the environment needed to run
++them.
++
+ Example usage:
+
+ def options(opt):
+@@ -20,15 +24,15 @@ def configure(conf):
+ conf.load('java javatest')
+
+ def build(bld):
+-
++
+ [ ... mainprog is built here ... ]
+
+ bld(features = 'javac javatest',
+- srcdir = 'test/',
+- outdir = 'test',
++ srcdir = 'test/',
++ outdir = 'test',
+ sourcepath = ['test'],
+- classpath = [ 'src' ],
+- basedir = 'test',
++ classpath = [ 'src' ],
++ basedir = 'test',
+ use = ['JAVATEST', 'mainprog'], # mainprog is the program being tested in src/
+ ut_str = 'java -cp ${CLASSPATH} ${JTRUNNER} ${SRC}',
+ jtest_source = bld.path.ant_glob('test/*.xml'),
+@@ -53,10 +57,107 @@ The runner class presence on the system is checked for at configuration stage.
+ """
+
+ import os
+-from waflib import Task, TaskGen, Options
++from waflib import Task, TaskGen, Options, Errors, Utils, Logs
++from waflib.Tools import ccroot
++
++JAR_RE = '**/*'
++
++def _process_use_rec(self, name):
++ """
++ Recursively process ``use`` for task generator with name ``name``..
++ Used by javatest_process_use.
++ """
++ if name in self.javatest_use_not or name in self.javatest_use_seen:
++ return
++ try:
++ tg = self.bld.get_tgen_by_name(name)
++ except Errors.WafError:
++ self.javatest_use_not.add(name)
++ return
++
++ self.javatest_use_seen.append(name)
++ tg.post()
++
++ for n in self.to_list(getattr(tg, 'use', [])):
++ _process_use_rec(self, n)
++
++@TaskGen.feature('javatest')
++@TaskGen.after_method('process_source', 'apply_link', 'use_javac_files')
++def javatest_process_use(self):
++ """
++ Process the ``use`` attribute which contains a list of task generator names and store
++ paths that later is used to populate the unit test runtime environment.
++ """
++ self.javatest_use_not = set()
++ self.javatest_use_seen = []
++ self.javatest_libpaths = [] # strings or Nodes
++ self.javatest_pypaths = [] # strings or Nodes
++ self.javatest_dep_nodes = []
++
++ names = self.to_list(getattr(self, 'use', []))
++ for name in names:
++ _process_use_rec(self, name)
++
++ def extend_unique(lst, varlst):
++ ext = []
++ for x in varlst:
++ if x not in lst:
++ ext.append(x)
++ lst.extend(ext)
++
++ # Collect type specific info needed to construct a valid runtime environment
++ # for the test.
++ for name in self.javatest_use_seen:
++ tg = self.bld.get_tgen_by_name(name)
++
++ # Python-Java embedding crosstools such as JEP
++ if 'py' in tg.features:
++ # Python dependencies are added to PYTHONPATH
++ pypath = getattr(tg, 'install_from', tg.path)
++
++ if 'buildcopy' in tg.features:
++ # Since buildcopy is used we assume that PYTHONPATH in build should be used,
++ # not source
++ extend_unique(self.javatest_pypaths, [pypath.get_bld().abspath()])
++
++ # Add buildcopy output nodes to dependencies
++ extend_unique(self.javatest_dep_nodes, [o for task in getattr(tg, 'tasks', []) for o in getattr(task, 'outputs', [])])
++ else:
++ # If buildcopy is not used, depend on sources instead
++ extend_unique(self.javatest_dep_nodes, tg.source)
++ extend_unique(self.javatest_pypaths, [pypath.abspath()])
++
++
++ if getattr(tg, 'link_task', None):
++ # For tasks with a link_task (C, C++, D et.c.) include their library paths:
++ if not isinstance(tg.link_task, ccroot.stlink_task):
++ extend_unique(self.javatest_dep_nodes, tg.link_task.outputs)
++ extend_unique(self.javatest_libpaths, tg.link_task.env.LIBPATH)
++
++ if 'pyext' in tg.features:
++ # If the taskgen is extending Python we also want to add the interpreter libpath.
++ extend_unique(self.javatest_libpaths, tg.link_task.env.LIBPATH_PYEXT)
++ else:
++ # Only add to libpath if the link task is not a Python extension
++ extend_unique(self.javatest_libpaths, [tg.link_task.outputs[0].parent.abspath()])
++
++ if 'javac' in tg.features or 'jar' in tg.features:
++ if hasattr(tg, 'jar_task'):
++ # For Java JAR tasks depend on generated JAR
++ extend_unique(self.javatest_dep_nodes, tg.jar_task.outputs)
++ else:
++ # For Java non-JAR ones we need to glob generated files (Java output files are not predictable)
++ if hasattr(tg, 'outdir'):
++ base_node = tg.outdir
++ else:
++ base_node = tg.path.get_bld()
++
++ self.javatest_dep_nodes.extend([dx for dx in base_node.ant_glob(JAR_RE, remove=False, quiet=True)])
++
++
+
+ @TaskGen.feature('javatest')
+-@TaskGen.after_method('apply_java', 'use_javac_files', 'set_classpath')
++@TaskGen.after_method('apply_java', 'use_javac_files', 'set_classpath', 'javatest_process_use')
+ def make_javatest(self):
+ """
+ Creates a ``utest`` task with a populated environment for Java Unit test execution
+@@ -65,6 +166,9 @@ def make_javatest(self):
+ tsk = self.create_task('utest')
+ tsk.set_run_after(self.javac_task)
+
++ # Dependencies from recursive use analysis
++ tsk.dep_nodes.extend(self.javatest_dep_nodes)
++
+ # Put test input files as waf_unit_test relies on that for some prints and log generation
+ # If jtest_source is there, this is specially useful for passing XML for TestNG
+ # that contain test specification, use that as inputs, otherwise test sources
+@@ -97,6 +201,21 @@ def make_javatest(self):
+
+ if not hasattr(self, 'ut_env'):
+ self.ut_env = dict(os.environ)
++ def add_paths(var, lst):
++ # Add list of paths to a variable, lst can contain strings or nodes
++ lst = [ str(n) for n in lst ]
++ Logs.debug("ut: %s: Adding paths %s=%s", self, var, lst)
++ self.ut_env[var] = os.pathsep.join(lst) + os.pathsep + self.ut_env.get(var, '')
++
++ add_paths('PYTHONPATH', self.javatest_pypaths)
++
++ if Utils.is_win32:
++ add_paths('PATH', self.javatest_libpaths)
++ elif Utils.unversioned_sys_platform() == 'darwin':
++ add_paths('DYLD_LIBRARY_PATH', self.javatest_libpaths)
++ add_paths('LD_LIBRARY_PATH', self.javatest_libpaths)
++ else:
++ add_paths('LD_LIBRARY_PATH', self.javatest_libpaths)
+
+ def configure(ctx):
+ cp = ctx.env.CLASSPATH or '.'
+diff --git third_party/waf/waflib/extras/msvc_pdb.py third_party/waf/waflib/extras/msvc_pdb.py
+new file mode 100644
+index 00000000000..077656b4f7e
+--- /dev/null
++++ third_party/waf/waflib/extras/msvc_pdb.py
+@@ -0,0 +1,46 @@
++#!/usr/bin/env python
++# encoding: utf-8
++# Rafaël Kooi 2019
++
++from waflib import TaskGen
++
++@TaskGen.feature('c', 'cxx', 'fc')
++@TaskGen.after_method('propagate_uselib_vars')
++def add_pdb_per_object(self):
++ """For msvc/fortran, specify a unique compile pdb per object, to work
++ around LNK4099. Flags are updated with a unique /Fd flag based on the
++ task output name. This is separate from the link pdb.
++ """
++ if not hasattr(self, 'compiled_tasks'):
++ return
++
++ link_task = getattr(self, 'link_task', None)
++
++ for task in self.compiled_tasks:
++ if task.inputs and task.inputs[0].name.lower().endswith('.rc'):
++ continue
++
++ add_pdb = False
++ for flagname in ('CFLAGS', 'CXXFLAGS', 'FCFLAGS'):
++ # several languages may be used at once
++ for flag in task.env[flagname]:
++ if flag[1:].lower() == 'zi':
++ add_pdb = True
++ break
++
++ if add_pdb:
++ node = task.outputs[0].change_ext('.pdb')
++ pdb_flag = '/Fd:' + node.abspath()
++
++ for flagname in ('CFLAGS', 'CXXFLAGS', 'FCFLAGS'):
++ buf = [pdb_flag]
++ for flag in task.env[flagname]:
++ if flag[1:3] == 'Fd' or flag[1:].lower() == 'fs' or flag[1:].lower() == 'mp':
++ continue
++ buf.append(flag)
++ task.env[flagname] = buf
++
++ if link_task and not node in link_task.dep_nodes:
++ link_task.dep_nodes.append(node)
++ if not node in task.outputs:
++ task.outputs.append(node)
+diff --git third_party/waf/waflib/extras/pytest.py third_party/waf/waflib/extras/pytest.py
+index 7dd5a1a087a..fc9ad1c23e4 100644
+--- third_party/waf/waflib/extras/pytest.py
++++ third_party/waf/waflib/extras/pytest.py
+@@ -40,6 +40,8 @@ the following environment variables for the `pytest` test runner:
+
+ - `pytest_libpath` attribute is used to manually specify additional linker paths.
+
++3. Java class search path (CLASSPATH) of any Java/Javalike dependency
++
+ Note: `pytest` cannot automatically determine the correct `PYTHONPATH` for `pyext` taskgens
+ because the extension might be part of a Python package or used standalone:
+
+@@ -119,6 +121,7 @@ def pytest_process_use(self):
+ self.pytest_use_seen = []
+ self.pytest_paths = [] # strings or Nodes
+ self.pytest_libpaths = [] # strings or Nodes
++ self.pytest_javapaths = [] # strings or Nodes
+ self.pytest_dep_nodes = []
+
+ names = self.to_list(getattr(self, 'use', []))
+@@ -157,6 +160,17 @@ def pytest_process_use(self):
+ extend_unique(self.pytest_dep_nodes, tg.source)
+ extend_unique(self.pytest_paths, [pypath.abspath()])
+
++ if 'javac' in tg.features:
++ # If a JAR is generated point to that, otherwise to directory
++ if getattr(tg, 'jar_task', None):
++ extend_unique(self.pytest_javapaths, [tg.jar_task.outputs[0].abspath()])
++ else:
++ extend_unique(self.pytest_javapaths, [tg.path.get_bld()])
++
++ # And add respective dependencies if present
++ if tg.use_lst:
++ extend_unique(self.pytest_javapaths, tg.use_lst)
++
+ if getattr(tg, 'link_task', None):
+ # For tasks with a link_task (C, C++, D et.c.) include their library paths:
+ if not isinstance(tg.link_task, ccroot.stlink_task):
+@@ -212,8 +226,9 @@ def make_pytest(self):
+ Logs.debug("ut: %s: Adding paths %s=%s", self, var, lst)
+ self.ut_env[var] = os.pathsep.join(lst) + os.pathsep + self.ut_env.get(var, '')
+
+- # Prepend dependency paths to PYTHONPATH and LD_LIBRARY_PATH
++ # Prepend dependency paths to PYTHONPATH, CLASSPATH and LD_LIBRARY_PATH
+ add_paths('PYTHONPATH', self.pytest_paths)
++ add_paths('CLASSPATH', self.pytest_javapaths)
+
+ if Utils.is_win32:
+ add_paths('PATH', self.pytest_libpaths)
+diff --git third_party/waf/waflib/extras/wafcache.py third_party/waf/waflib/extras/wafcache.py
+new file mode 100644
+index 00000000000..8b9567faf14
+--- /dev/null
++++ third_party/waf/waflib/extras/wafcache.py
+@@ -0,0 +1,524 @@
++#! /usr/bin/env python
++# encoding: utf-8
++# Thomas Nagy, 2019 (ita)
++
++"""
++Filesystem-based cache system to share and re-use build artifacts
++
++Cache access operations (copy to and from) are delegated to
++independent pre-forked worker subprocesses.
++
++The following environment variables may be set:
++* WAFCACHE: several possibilities:
++ - File cache:
++ absolute path of the waf cache (~/.cache/wafcache_user,
++ where `user` represents the currently logged-in user)
++ - URL to a cache server, for example:
++ export WAFCACHE=http://localhost:8080/files/
++ in that case, GET/POST requests are made to urls of the form
++ http://localhost:8080/files/000000000/0 (cache management is then up to the server)
++ - GCS or S3 bucket
++ gs://my-bucket/
++ s3://my-bucket/
++* WAFCACHE_NO_PUSH: if set, disables pushing to the cache
++* WAFCACHE_VERBOSITY: if set, displays more detailed cache operations
++
++File cache specific options:
++ Files are copied using hard links by default; if the cache is located
++ onto another partition, the system switches to file copies instead.
++* WAFCACHE_TRIM_MAX_FOLDER: maximum amount of tasks to cache (1M)
++* WAFCACHE_EVICT_MAX_BYTES: maximum amount of cache size in bytes (10GB)
++* WAFCACHE_EVICT_INTERVAL_MINUTES: minimum time interval to try
++ and trim the cache (3 minutess)
++Usage::
++
++ def build(bld):
++ bld.load('wafcache')
++ ...
++
++To troubleshoot::
++
++ waf clean build --zones=wafcache
++"""
++
++import atexit, base64, errno, fcntl, getpass, os, shutil, sys, time, traceback, urllib3
++try:
++ import subprocess32 as subprocess
++except ImportError:
++ import subprocess
++
++base_cache = os.path.expanduser('~/.cache/')
++if not os.path.isdir(base_cache):
++ base_cache = '/tmp/'
++default_wafcache_dir = os.path.join(base_cache, 'wafcache_' + getpass.getuser())
++
++CACHE_DIR = os.environ.get('WAFCACHE', default_wafcache_dir)
++TRIM_MAX_FOLDERS = int(os.environ.get('WAFCACHE_TRIM_MAX_FOLDER', 1000000))
++EVICT_INTERVAL_MINUTES = int(os.environ.get('WAFCACHE_EVICT_INTERVAL_MINUTES', 3))
++EVICT_MAX_BYTES = int(os.environ.get('WAFCACHE_EVICT_MAX_BYTES', 10**10))
++WAFCACHE_NO_PUSH = 1 if os.environ.get('WAFCACHE_NO_PUSH') else 0
++WAFCACHE_VERBOSITY = 1 if os.environ.get('WAFCACHE_VERBOSITY') else 0
++OK = "ok"
++
++try:
++ import cPickle
++except ImportError:
++ import pickle as cPickle
++
++if __name__ != '__main__':
++ from waflib import Task, Logs, Utils, Build
++
++def can_retrieve_cache(self):
++ """
++ New method for waf Task classes
++ """
++ if not self.outputs:
++ return False
++
++ self.cached = False
++
++ sig = self.signature()
++ ssig = Utils.to_hex(self.uid() + sig)
++
++ files_to = [node.abspath() for node in self.outputs]
++ err = cache_command(ssig, [], files_to)
++ if err.startswith(OK):
++ if WAFCACHE_VERBOSITY:
++ Logs.pprint('CYAN', ' Fetched %r from cache' % files_to)
++ else:
++ Logs.debug('wafcache: fetched %r from cache', files_to)
++ else:
++ if WAFCACHE_VERBOSITY:
++ Logs.pprint('YELLOW', ' No cache entry %s' % files_to)
++ else:
++ Logs.debug('wafcache: No cache entry %s: %s', files_to, err)
++ return False
++
++ self.cached = True
++ return True
++
++def put_files_cache(self):
++ """
++ New method for waf Task classes
++ """
++ if WAFCACHE_NO_PUSH or getattr(self, 'cached', None) or not self.outputs:
++ return
++
++ bld = self.generator.bld
++ sig = self.signature()
++ ssig = Utils.to_hex(self.uid() + sig)
++
++ files_from = [node.abspath() for node in self.outputs]
++ err = cache_command(ssig, files_from, [])
++
++ if err.startswith(OK):
++ if WAFCACHE_VERBOSITY:
++ Logs.pprint('CYAN', ' Successfully uploaded %s to cache' % files_from)
++ else:
++ Logs.debug('wafcache: Successfully uploaded %r to cache', files_from)
++ else:
++ if WAFCACHE_VERBOSITY:
++ Logs.pprint('RED', ' Error caching step results %s: %s' % (files_from, err))
++ else:
++ Logs.debug('wafcache: Error caching results %s: %s', files_from, err)
++
++ bld.task_sigs[self.uid()] = self.cache_sig
++
++def hash_env_vars(self, env, vars_lst):
++ """
++ Reimplement BuildContext.hash_env_vars so that the resulting hash does not depend on local paths
++ """
++ if not env.table:
++ env = env.parent
++ if not env:
++ return Utils.SIG_NIL
++
++ idx = str(id(env)) + str(vars_lst)
++ try:
++ cache = self.cache_env
++ except AttributeError:
++ cache = self.cache_env = {}
++ else:
++ try:
++ return self.cache_env[idx]
++ except KeyError:
++ pass
++
++ v = str([env[a] for a in vars_lst])
++ v = v.replace(self.srcnode.abspath().__repr__()[:-1], '')
++ m = Utils.md5()
++ m.update(v.encode())
++ ret = m.digest()
++
++ Logs.debug('envhash: %r %r', ret, v)
++
++ cache[idx] = ret
++
++ return ret
++
++def uid(self):
++ """
++ Reimplement Task.uid() so that the signature does not depend on local paths
++ """
++ try:
++ return self.uid_
++ except AttributeError:
++ m = Utils.md5()
++ src = self.generator.bld.srcnode
++ up = m.update
++ up(self.__class__.__name__.encode())
++ for x in self.inputs + self.outputs:
++ up(x.path_from(src).encode())
++ self.uid_ = m.digest()
++ return self.uid_
++
++
++def make_cached(cls):
++ """
++ Enable the waf cache for a given task class
++ """
++ if getattr(cls, 'nocache', None) or getattr(cls, 'has_cache', False):
++ return
++
++ m1 = getattr(cls, 'run', None)
++ def run(self):
++ if getattr(self, 'nocache', False):
++ return m1(self)
++ if self.can_retrieve_cache():
++ return 0
++ return m1(self)
++ cls.run = run
++
++ m2 = getattr(cls, 'post_run', None)
++ def post_run(self):
++ if getattr(self, 'nocache', False):
++ return m2(self)
++ ret = m2(self)
++ self.put_files_cache()
++ if hasattr(self, 'chmod'):
++ for node in self.outputs:
++ os.chmod(node.abspath(), self.chmod)
++ return ret
++ cls.post_run = post_run
++ cls.has_cache = True
++
++process_pool = []
++def get_process():
++ """
++ Returns a worker process that can process waf cache commands
++ The worker process is assumed to be returned to the process pool when unused
++ """
++ try:
++ return process_pool.pop()
++ except IndexError:
++ filepath = os.path.dirname(os.path.abspath(__file__)) + os.sep + 'wafcache.py'
++ cmd = [sys.executable, '-c', Utils.readf(filepath)]
++ return subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, bufsize=0)
++
++def atexit_pool():
++ for k in process_pool:
++ try:
++ os.kill(k.pid, 9)
++ except OSError:
++ pass
++ else:
++ k.wait()
++atexit.register(atexit_pool)
++
++def build(bld):
++ """
++ Called during the build process to enable file caching
++ """
++ if process_pool:
++ # already called once
++ return
++
++ for x in range(bld.jobs):
++ process_pool.append(get_process())
++
++ Task.Task.can_retrieve_cache = can_retrieve_cache
++ Task.Task.put_files_cache = put_files_cache
++ Task.Task.uid = uid
++ Build.BuildContext.hash_env_vars = hash_env_vars
++ for x in reversed(list(Task.classes.values())):
++ make_cached(x)
++
++def cache_command(sig, files_from, files_to):
++ """
++ Create a command for cache worker processes, returns a pickled
++ base64-encoded tuple containing the task signature, a list of files to
++ cache and a list of files files to get from cache (one of the lists
++ is assumed to be empty)
++ """
++ proc = get_process()
++
++ obj = base64.b64encode(cPickle.dumps([sig, files_from, files_to]))
++ proc.stdin.write(obj)
++ proc.stdin.write('\n'.encode())
++ proc.stdin.flush()
++ obj = proc.stdout.readline()
++ if not obj:
++ raise OSError('Preforked sub-process %r died' % proc.pid)
++ process_pool.append(proc)
++ return cPickle.loads(base64.b64decode(obj))
++
++try:
++ copyfun = os.link
++except NameError:
++ copyfun = shutil.copy2
++
++def atomic_copy(orig, dest):
++ """
++ Copy files to the cache, the operation is atomic for a given file
++ """
++ global copyfun
++ tmp = dest + '.tmp'
++ up = os.path.dirname(dest)
++ try:
++ os.makedirs(up)
++ except OSError:
++ pass
++
++ try:
++ copyfun(orig, tmp)
++ except OSError as e:
++ if e.errno == errno.EXDEV:
++ copyfun = shutil.copy2
++ copyfun(orig, tmp)
++ else:
++ raise
++ os.rename(tmp, dest)
++
++def lru_trim():
++ """
++ the cache folders take the form:
++ `CACHE_DIR/0b/0b180f82246d726ece37c8ccd0fb1cde2650d7bfcf122ec1f169079a3bfc0ab9`
++ they are listed in order of last access, and then removed
++ until the amount of folders is within TRIM_MAX_FOLDERS and the total space
++ taken by files is less than EVICT_MAX_BYTES
++ """
++ lst = []
++ for up in os.listdir(CACHE_DIR):
++ if len(up) == 2:
++ sub = os.path.join(CACHE_DIR, up)
++ for hval in os.listdir(sub):
++ path = os.path.join(sub, hval)
++
++ size = 0
++ for fname in os.listdir(path):
++ size += os.lstat(os.path.join(path, fname)).st_size
++ lst.append((os.stat(path).st_mtime, size, path))
++
++ lst.sort(key=lambda x: x[0])
++ lst.reverse()
++
++ tot = sum(x[1] for x in lst)
++ while tot > EVICT_MAX_BYTES or len(lst) > TRIM_MAX_FOLDERS:
++ _, tmp_size, path = lst.pop()
++ tot -= tmp_size
++
++ tmp = path + '.tmp'
++ try:
++ shutil.rmtree(tmp)
++ except OSError:
++ pass
++ try:
++ os.rename(path, tmp)
++ except OSError:
++ sys.stderr.write('Could not rename %r to %r' % (path, tmp))
++ else:
++ try:
++ shutil.rmtree(tmp)
++ except OSError:
++ sys.stderr.write('Could not remove %r' % tmp)
++ sys.stderr.write("Cache trimmed: %r bytes in %r folders left\n" % (tot, len(lst)))
++
++
++def lru_evict():
++ """
++ Reduce the cache size
++ """
++ lockfile = os.path.join(CACHE_DIR, 'all.lock')
++ try:
++ st = os.stat(lockfile)
++ except EnvironmentError as e:
++ if e.errno == errno.ENOENT:
++ with open(lockfile, 'w') as f:
++ f.write('')
++ return
++ else:
++ raise
++
++ if st.st_mtime < time.time() - EVICT_INTERVAL_MINUTES * 60:
++ # check every EVICT_INTERVAL_MINUTES minutes if the cache is too big
++ # OCLOEXEC is unnecessary because no processes are spawned
++ fd = os.open(lockfile, os.O_RDWR | os.O_CREAT, 0o755)
++ try:
++ try:
++ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
++ except EnvironmentError:
++ sys.stderr.write('another process is running!\n')
++ pass
++ else:
++ # now dow the actual cleanup
++ lru_trim()
++ os.utime(lockfile, None)
++ finally:
++ os.close(fd)
++
++class netcache(object):
++ def __init__(self):
++ self.http = urllib3.PoolManager()
++
++ def url_of(self, sig, i):
++ return "%s/%s/%s" % (CACHE_DIR, sig, i)
++
++ def upload(self, file_path, sig, i):
++ url = self.url_of(sig, i)
++ with open(file_path, 'rb') as f:
++ file_data = f.read()
++ r = self.http.request('POST', url, timeout=60,
++ fields={ 'file': ('%s/%s' % (sig, i), file_data), })
++ if r.status >= 400:
++ raise OSError("Invalid status %r %r" % (url, r.status))
++
++ def download(self, file_path, sig, i):
++ url = self.url_of(sig, i)
++ with self.http.request('GET', url, preload_content=False, timeout=60) as inf:
++ if inf.status >= 400:
++ raise OSError("Invalid status %r %r" % (url, inf.status))
++ with open(file_path, 'wb') as out:
++ shutil.copyfileobj(inf, out)
++
++ def copy_to_cache(self, sig, files_from, files_to):
++ try:
++ for i, x in enumerate(files_from):
++ if not os.path.islink(x):
++ self.upload(x, sig, i)
++ except Exception:
++ return traceback.format_exc()
++ return OK
++
++ def copy_from_cache(self, sig, files_from, files_to):
++ try:
++ for i, x in enumerate(files_to):
++ self.download(x, sig, i)
++ except Exception:
++ return traceback.format_exc()
++ return OK
++
++class fcache(object):
++ def __init__(self):
++ if not os.path.exists(CACHE_DIR):
++ os.makedirs(CACHE_DIR)
++ if not os.path.exists(CACHE_DIR):
++ raise ValueError('Could not initialize the cache directory')
++
++ def copy_to_cache(self, sig, files_from, files_to):
++ """
++ Copy files to the cache, existing files are overwritten,
++ and the copy is atomic only for a given file, not for all files
++ that belong to a given task object
++ """
++ try:
++ for i, x in enumerate(files_from):
++ dest = os.path.join(CACHE_DIR, sig[:2], sig, str(i))
++ atomic_copy(x, dest)
++ except Exception:
++ return traceback.format_exc()
++ else:
++ # attempt trimming if caching was successful:
++ # we may have things to trim!
++ lru_evict()
++ return OK
++
++ def copy_from_cache(self, sig, files_from, files_to):
++ """
++ Copy files from the cache
++ """
++ try:
++ for i, x in enumerate(files_to):
++ orig = os.path.join(CACHE_DIR, sig[:2], sig, str(i))
++ atomic_copy(orig, x)
++
++ # success! update the cache time
++ os.utime(os.path.join(CACHE_DIR, sig[:2], sig), None)
++ except Exception:
++ return traceback.format_exc()
++ return OK
++
++class bucket_cache(object):
++ def bucket_copy(self, source, target):
++ if CACHE_DIR.startswith('s3://'):
++ cmd = ['aws', 's3', 'cp', source, target]
++ else:
++ cmd = ['gsutil', 'cp', source, target]
++ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
++ out, err = proc.communicate()
++ if proc.returncode:
++ raise OSError('Error copy %r to %r using: %r (exit %r):\n out:%s\n err:%s' % (
++ source, target, cmd, proc.returncode, out.decode(), err.decode()))
++
++ def copy_to_cache(self, sig, files_from, files_to):
++ try:
++ for i, x in enumerate(files_from):
++ dest = os.path.join(CACHE_DIR, sig[:2], sig, str(i))
++ self.bucket_copy(x, dest)
++ except Exception:
++ return traceback.format_exc()
++ return OK
++
++ def copy_from_cache(self, sig, files_from, files_to):
++ try:
++ for i, x in enumerate(files_to):
++ orig = os.path.join(CACHE_DIR, sig[:2], sig, str(i))
++ self.bucket_copy(orig, x)
++ except EnvironmentError:
++ return traceback.format_exc()
++ return OK
++
++def loop(service):
++ """
++ This function is run when this file is run as a standalone python script,
++ it assumes a parent process that will communicate the commands to it
++ as pickled-encoded tuples (one line per command)
++
++ The commands are to copy files to the cache or copy files from the
++ cache to a target destination
++ """
++ # one operation is performed at a single time by a single process
++ # therefore stdin never has more than one line
++ txt = sys.stdin.readline().strip()
++ if not txt:
++ # parent process probably ended
++ sys.exit(1)
++ ret = OK
++
++ [sig, files_from, files_to] = cPickle.loads(base64.b64decode(txt))
++ if files_from:
++ # TODO return early when pushing files upstream
++ ret = service.copy_to_cache(sig, files_from, files_to)
++ elif files_to:
++ # the build process waits for workers to (possibly) obtain files from the cache
++ ret = service.copy_from_cache(sig, files_from, files_to)
++ else:
++ ret = "Invalid command"
++
++ obj = base64.b64encode(cPickle.dumps(ret))
++ sys.stdout.write(obj.decode())
++ sys.stdout.write('\n')
++ sys.stdout.flush()
++
++if __name__ == '__main__':
++ if CACHE_DIR.startswith('s3://') or CACHE_DIR.startswith('gs://'):
++ service = bucket_cache()
++ elif CACHE_DIR.startswith('http'):
++ service = netcache()
++ else:
++ service = fcache()
++ while 1:
++ try:
++ loop(service)
++ except KeyboardInterrupt:
++ break
++
+--
+2.37.3
+
diff --git a/net/samba413/files/patch-waf-2.0.21 b/net/samba413/files/patch-waf-2.0.21
new file mode 100644
index 000000000000..01b2d6e6cafe
--- /dev/null
+++ b/net/samba413/files/patch-waf-2.0.21
@@ -0,0 +1,703 @@
+From 6718b5e6d059e5668fc538be802ebd9fbe5ce9af Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Wed, 25 Nov 2020 16:29:06 +0100
+Subject: [PATCH] waf: upgrade to 2.0.21
+
+This commit message was wrong:
+
+ commit 5fc3a71d0f54b176d3cb2e399718d0468507e797
+ Author: David Mulder <dmulder@suse.com>
+ Date: Mon Aug 24 13:12:46 2020 -0600
+
+ waf: upgrade to 2.0.20
+
+ This contain an important change:
+ "Fix gccdeps.scan() returning nodes that no longer exist on disk."
+ https://gitlab.com/ita1024/waf/-/merge_requests/2293
+
+ Signed-off-by: David Mulder <dmulder@suse.com>
+ Reviewed-by: Stefan Metzmacher <metze@samba.org>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+
+The fix was in in waf master, but not included in 2.0.20,
+but it's now included in 2.0.21.
+
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+---
+ buildtools/bin/waf | 2 +-
+ buildtools/wafsamba/wafsamba.py | 2 +-
+ third_party/waf/waflib/Build.py | 6 ++-
+ third_party/waf/waflib/Context.py | 8 ++--
+ third_party/waf/waflib/Tools/asm.py | 5 +-
+ third_party/waf/waflib/Tools/c_config.py | 1 +
+ third_party/waf/waflib/Tools/msvc.py | 8 +++-
+ third_party/waf/waflib/Tools/qt5.py | 26 +++++++++--
+ third_party/waf/waflib/Tools/waf_unit_test.py | 10 +++-
+ third_party/waf/waflib/extras/boost.py | 5 +-
+ .../waf/waflib/extras/c_dumbpreproc.py | 2 +-
+ third_party/waf/waflib/extras/doxygen.py | 4 +-
+ .../waf/waflib/extras/file_to_object.py | 9 +++-
+ third_party/waf/waflib/extras/gccdeps.py | 21 +++++++--
+ third_party/waf/waflib/extras/msvcdeps.py | 27 +++++++++--
+ third_party/waf/waflib/extras/pch.py | 4 +-
+ third_party/waf/waflib/extras/sphinx.py | 40 ++++++++++++----
+ third_party/waf/waflib/extras/wafcache.py | 46 +++++++++++++++----
+ third_party/waf/waflib/extras/xcode6.py | 18 ++++----
+ 19 files changed, 181 insertions(+), 63 deletions(-)
+
+diff --git buildtools/bin/waf buildtools/bin/waf
+index feabe25d131..041450fc131 100755
+--- buildtools/bin/waf
++++ buildtools/bin/waf
+@@ -32,7 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
+
+ import os, sys, inspect
+
+-VERSION="2.0.20"
++VERSION="2.0.21"
+ REVISION="x"
+ GIT="x"
+ INSTALL="x"
+diff --git buildtools/wafsamba/wafsamba.py buildtools/wafsamba/wafsamba.py
+index 9dd6d05b91b..d1baa3b4940 100644
+--- buildtools/wafsamba/wafsamba.py
++++ buildtools/wafsamba/wafsamba.py
+@@ -38,7 +38,7 @@ LIB_PATH="shared"
+
+ os.environ['PYTHONUNBUFFERED'] = '1'
+
+-if Context.HEXVERSION not in (0x2001400,):
++if Context.HEXVERSION not in (0x2001500,):
+ Logs.error('''
+ Please use the version of waf that comes with Samba, not
+ a system installed version. See http://wiki.samba.org/index.php/Waf
+diff --git third_party/waf/waflib/Build.py third_party/waf/waflib/Build.py
+index 39f0991918b..52837618577 100644
+--- third_party/waf/waflib/Build.py
++++ third_party/waf/waflib/Build.py
+@@ -753,10 +753,12 @@ class BuildContext(Context.Context):
+ else:
+ ln = self.launch_node()
+ if ln.is_child_of(self.bldnode):
+- Logs.warn('Building from the build directory, forcing --targets=*')
++ if Logs.verbose > 1:
++ Logs.warn('Building from the build directory, forcing --targets=*')
+ ln = self.srcnode
+ elif not ln.is_child_of(self.srcnode):
+- Logs.warn('CWD %s is not under %s, forcing --targets=* (run distclean?)', ln.abspath(), self.srcnode.abspath())
++ if Logs.verbose > 1:
++ Logs.warn('CWD %s is not under %s, forcing --targets=* (run distclean?)', ln.abspath(), self.srcnode.abspath())
+ ln = self.srcnode
+
+ def is_post(tg, ln):
+diff --git third_party/waf/waflib/Context.py third_party/waf/waflib/Context.py
+index 3f1b4fa48ab..0ce9df6e91f 100644
+--- third_party/waf/waflib/Context.py
++++ third_party/waf/waflib/Context.py
+@@ -18,13 +18,13 @@ else:
+ import imp
+
+ # the following 3 constants are updated on each new release (do not touch)
+-HEXVERSION=0x2001400
++HEXVERSION=0x2001500
+ """Constant updated on new releases"""
+
+-WAFVERSION="2.0.20"
++WAFVERSION="2.0.21"
+ """Constant updated on new releases"""
+
+-WAFREVISION="668769470956da8c5b60817cb8884cd7d0f87cd4"
++WAFREVISION="edde20a6425a5c3eb6b47d5f3f5c4fbc93fed5f4"
+ """Git revision when the waf version is updated"""
+
+ WAFNAME="waf"
+@@ -530,7 +530,7 @@ class Context(ctx):
+ """
+ Prints a configuration message of the form ``msg: result``.
+ The second part of the message will be in colors. The output
+- can be disabled easly by setting ``in_msg`` to a positive value::
++ can be disabled easily by setting ``in_msg`` to a positive value::
+
+ def configure(conf):
+ self.in_msg = 1
+diff --git third_party/waf/waflib/Tools/asm.py third_party/waf/waflib/Tools/asm.py
+index a57e83bb5ec..1d34ddaca7f 100644
+--- third_party/waf/waflib/Tools/asm.py
++++ third_party/waf/waflib/Tools/asm.py
+@@ -56,13 +56,11 @@ class asm(Task.Task):
+ Compiles asm files by gas/nasm/yasm/...
+ """
+ color = 'BLUE'
+- run_str = '${AS} ${ASFLAGS} ${ASMPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${AS_SRC_F}${SRC} ${AS_TGT_F}${TGT}'
++ run_str = '${AS} ${ASFLAGS} ${ASMPATH_ST:INCPATHS} ${ASMDEFINES_ST:DEFINES} ${AS_SRC_F}${SRC} ${AS_TGT_F}${TGT}'
+
+ def scan(self):
+ if self.env.ASM_NAME == 'gas':
+ return c_preproc.scan(self)
+- Logs.warn('There is no dependency scanner for Nasm!')
+- return [[], []]
+ elif self.env.ASM_NAME == 'nasm':
+ Logs.warn('The Nasm dependency scanner is incomplete!')
+
+@@ -106,3 +104,4 @@ class asmstlib(stlink_task):
+
+ def configure(conf):
+ conf.env.ASMPATH_ST = '-I%s'
++ conf.env.ASMDEFINES_ST = '-D%s'
+diff --git third_party/waf/waflib/Tools/c_config.py third_party/waf/waflib/Tools/c_config.py
+index 98187fac2e2..03b6bf61bc0 100644
+--- third_party/waf/waflib/Tools/c_config.py
++++ third_party/waf/waflib/Tools/c_config.py
+@@ -68,6 +68,7 @@ MACRO_TO_DEST_CPU = {
+ '__s390__' : 's390',
+ '__sh__' : 'sh',
+ '__xtensa__' : 'xtensa',
++'__e2k__' : 'e2k',
+ }
+
+ @conf
+diff --git third_party/waf/waflib/Tools/msvc.py third_party/waf/waflib/Tools/msvc.py
+index f169c7f441b..37233be8242 100644
+--- third_party/waf/waflib/Tools/msvc.py
++++ third_party/waf/waflib/Tools/msvc.py
+@@ -99,7 +99,13 @@ all_icl_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'),
+ """List of icl platforms"""
+
+ def options(opt):
+- opt.add_option('--msvc_version', type='string', help = 'msvc version, eg: "msvc 10.0,msvc 9.0"', default='')
++ default_ver = ''
++ vsver = os.getenv('VSCMD_VER')
++ if vsver:
++ m = re.match(r'(^\d+\.\d+).*', vsver)
++ if m:
++ default_ver = 'msvc %s' % m.group(1)
++ opt.add_option('--msvc_version', type='string', help = 'msvc version, eg: "msvc 10.0,msvc 9.0"', default=default_ver)
+ opt.add_option('--msvc_targets', type='string', help = 'msvc targets, eg: "x64,arm"', default='')
+ opt.add_option('--no-msvc-lazy', action='store_false', help = 'lazily check msvc target environments', default=True, dest='msvc_lazy')
+
+diff --git third_party/waf/waflib/Tools/qt5.py third_party/waf/waflib/Tools/qt5.py
+index 99e021bae61..cff2028174f 100644
+--- third_party/waf/waflib/Tools/qt5.py
++++ third_party/waf/waflib/Tools/qt5.py
+@@ -57,7 +57,23 @@ A few options (--qt{dir,bin,...}) and environment variables
+ (QT5_{ROOT,DIR,MOC,UIC,XCOMPILE}) allow finer tuning of the tool,
+ tool path selection, etc; please read the source for more info.
+
+-The detection uses pkg-config on Linux by default. To force static library detection use:
++The detection uses pkg-config on Linux by default. The list of
++libraries to be requested to pkg-config is formulated by scanning
++in the QTLIBS directory (that can be passed via --qtlibs or by
++setting the environment variable QT5_LIBDIR otherwise is derived
++by querying qmake for QT_INSTALL_LIBS directory) for shared/static
++libraries present.
++Alternatively the list of libraries to be requested via pkg-config
++can be set using the qt5_vars attribute, ie:
++
++ conf.qt5_vars = ['Qt5Core', 'Qt5Gui', 'Qt5Widgets', 'Qt5Test'];
++
++This can speed up configuration phase if needed libraries are
++known beforehand, can improve detection on systems with a
++sparse QT5 libraries installation (ie. NIX) and can improve
++detection of some header-only Qt modules (ie. Qt5UiPlugin).
++
++To force static library detection use:
+ QT5_XCOMPILE=1 QT5_FORCE_STATIC=1 waf configure
+ """
+
+@@ -466,6 +482,9 @@ def configure(self):
+
+ The detection uses the program ``pkg-config`` through :py:func:`waflib.Tools.config_c.check_cfg`
+ """
++ if 'COMPILER_CXX' not in self.env:
++ self.fatal('No CXX compiler defined: did you forget to configure compiler_cxx first?')
++
+ self.find_qt5_binaries()
+ self.set_qt5_libs_dir()
+ self.set_qt5_libs_to_check()
+@@ -478,9 +497,6 @@ def configure(self):
+ if not has_xml:
+ Logs.error('No xml.sax support was found, rcc dependencies will be incomplete!')
+
+- if 'COMPILER_CXX' not in self.env:
+- self.fatal('No CXX compiler defined: did you forget to configure compiler_cxx first?')
+-
+ # Qt5 may be compiled with '-reduce-relocations' which requires dependent programs to have -fPIE or -fPIC?
+ frag = '#include <QMap>\nint main(int argc, char **argv) {QMap<int,int> m;return m.keys().size();}\n'
+ uses = 'QT5CORE'
+@@ -637,7 +653,7 @@ def set_qt5_libs_dir(self):
+ except Errors.WafError:
+ qtdir = self.cmd_and_log(env.QMAKE + ['-query', 'QT_INSTALL_PREFIX']).strip()
+ qtlibs = os.path.join(qtdir, 'lib')
+- self.msg('Found the Qt5 libraries in', qtlibs)
++ self.msg('Found the Qt5 library path', qtlibs)
+ env.QTLIBS = qtlibs
+
+ @conf
+diff --git third_party/waf/waflib/Tools/waf_unit_test.py third_party/waf/waflib/Tools/waf_unit_test.py
+index 6ff6f72739f..dc66fe9c184 100644
+--- third_party/waf/waflib/Tools/waf_unit_test.py
++++ third_party/waf/waflib/Tools/waf_unit_test.py
+@@ -97,6 +97,7 @@ def make_interpreted_test(self):
+ if isinstance(v, str):
+ v = v.split(os.pathsep)
+ self.ut_env[k] = os.pathsep.join(p + v)
++ self.env.append_value('UT_DEPS', ['%r%r' % (key, self.ut_env[key]) for key in self.ut_env])
+
+ @feature('test')
+ @after_method('apply_link', 'process_use')
+@@ -108,7 +109,8 @@ def make_test(self):
+ tsk = self.create_task('utest', self.link_task.outputs)
+ if getattr(self, 'ut_str', None):
+ self.ut_run, lst = Task.compile_fun(self.ut_str, shell=getattr(self, 'ut_shell', False))
+- tsk.vars = lst + tsk.vars
++ tsk.vars = tsk.vars + lst
++ self.env.append_value('UT_DEPS', self.ut_str)
+
+ self.handle_ut_cwd('ut_cwd')
+
+@@ -139,6 +141,10 @@ def make_test(self):
+ if not hasattr(self, 'ut_cmd'):
+ self.ut_cmd = getattr(Options.options, 'testcmd', False)
+
++ self.env.append_value('UT_DEPS', str(self.ut_cmd))
++ self.env.append_value('UT_DEPS', self.ut_paths)
++ self.env.append_value('UT_DEPS', ['%r%r' % (key, self.ut_env[key]) for key in self.ut_env])
++
+ @taskgen_method
+ def add_test_results(self, tup):
+ """Override and return tup[1] to interrupt the build immediately if a test does not run"""
+@@ -159,7 +165,7 @@ class utest(Task.Task):
+ """
+ color = 'PINK'
+ after = ['vnum', 'inst']
+- vars = []
++ vars = ['UT_DEPS']
+
+ def runnable_status(self):
+ """
+diff --git third_party/waf/waflib/extras/boost.py third_party/waf/waflib/extras/boost.py
+index c2aaaa938a2..93b312a1e6e 100644
+--- third_party/waf/waflib/extras/boost.py
++++ third_party/waf/waflib/extras/boost.py
+@@ -270,10 +270,12 @@ def boost_get_libs(self, *k, **kw):
+ return file
+ return None
+
++ # extensions from Tools.ccroot.lib_patterns
++ wo_ext = re.compile(r"\.(a|so|lib|dll|dylib)(\.[0-9\.]+)?$")
+ def format_lib_name(name):
+ if name.startswith('lib') and self.env.CC_NAME != 'msvc':
+ name = name[3:]
+- return name[:name.rfind('.')]
++ return wo_ext.sub("", name)
+
+ def match_libs(lib_names, is_static):
+ libs = []
+@@ -522,4 +524,3 @@ def install_boost(self):
+ except:
+ continue
+ install_boost.done = False
+-
+diff --git third_party/waf/waflib/extras/c_dumbpreproc.py third_party/waf/waflib/extras/c_dumbpreproc.py
+index ce9e1a400b9..1fdd5c364ae 100644
+--- third_party/waf/waflib/extras/c_dumbpreproc.py
++++ third_party/waf/waflib/extras/c_dumbpreproc.py
+@@ -66,7 +66,7 @@ class dumb_parser(parser):
+ if x == c_preproc.POPFILE:
+ self.currentnode_stack.pop()
+ continue
+- self.tryfind(y)
++ self.tryfind(y, env=env)
+
+ c_preproc.c_parser = dumb_parser
+
+diff --git third_party/waf/waflib/extras/doxygen.py third_party/waf/waflib/extras/doxygen.py
+index de75bc2738a..0fda70361f3 100644
+--- third_party/waf/waflib/extras/doxygen.py
++++ third_party/waf/waflib/extras/doxygen.py
+@@ -208,10 +208,10 @@ def process_doxy(self):
+ self.bld.fatal('doxygen file %s not found' % self.doxyfile)
+
+ # the task instance
+- dsk = self.create_task('doxygen', node)
++ dsk = self.create_task('doxygen', node, always_run=getattr(self, 'always', False))
+
+ if getattr(self, 'doxy_tar', None):
+- tsk = self.create_task('tar')
++ tsk = self.create_task('tar', always_run=getattr(self, 'always', False))
+ tsk.input_tasks = [dsk]
+ tsk.set_outputs(self.path.find_or_declare(self.doxy_tar))
+ if self.doxy_tar.endswith('bz2'):
+diff --git third_party/waf/waflib/extras/file_to_object.py third_party/waf/waflib/extras/file_to_object.py
+index 1393b511d63..13d2aef37df 100644
+--- third_party/waf/waflib/extras/file_to_object.py
++++ third_party/waf/waflib/extras/file_to_object.py
+@@ -31,7 +31,7 @@ Known issues:
+
+ """
+
+-import os
++import os, sys
+ from waflib import Task, TaskGen, Errors
+
+ def filename_c_escape(x):
+@@ -95,12 +95,17 @@ class file_to_object_c(Task.Task):
+
+ name = "_binary_" + "".join(name)
+
++ def char_to_num(ch):
++ if sys.version_info[0] < 3:
++ return ord(ch)
++ return ch
++
+ data = self.inputs[0].read('rb')
+ lines, line = [], []
+ for idx_byte, byte in enumerate(data):
+ line.append(byte)
+ if len(line) > 15 or idx_byte == size-1:
+- lines.append(", ".join(("0x%02x" % ord(x)) for x in line))
++ lines.append(", ".join(("0x%02x" % char_to_num(x)) for x in line))
+ line = []
+ data = ",\n ".join(lines)
+
+diff --git third_party/waf/waflib/extras/gccdeps.py third_party/waf/waflib/extras/gccdeps.py
+index c3a809e252a..1fc9373489a 100644
+--- third_party/waf/waflib/extras/gccdeps.py
++++ third_party/waf/waflib/extras/gccdeps.py
+@@ -163,10 +163,25 @@ def post_run(self):
+ def sig_implicit_deps(self):
+ if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
+ return super(self.derived_gccdeps, self).sig_implicit_deps()
++ bld = self.generator.bld
++
+ try:
+- return Task.Task.sig_implicit_deps(self)
+- except Errors.WafError:
+- return Utils.SIG_NIL
++ return self.compute_sig_implicit_deps()
++ except Errors.TaskNotReady:
++ raise ValueError("Please specify the build order precisely with gccdeps (asm/c/c++ tasks)")
++ except EnvironmentError:
++ # If a file is renamed, assume the dependencies are stale and must be recalculated
++ for x in bld.node_deps.get(self.uid(), []):
++ if not x.is_bld() and not x.exists():
++ try:
++ del x.parent.children[x.name]
++ except KeyError:
++ pass
++
++ key = self.uid()
++ bld.node_deps[key] = []
++ bld.raw_deps[key] = []
++ return Utils.SIG_NIL
+
+ def wrap_compiled_task(classname):
+ derived_class = type(classname, (Task.classes[classname],), {})
+diff --git third_party/waf/waflib/extras/msvcdeps.py third_party/waf/waflib/extras/msvcdeps.py
+index 873a4193150..52985dce058 100644
+--- third_party/waf/waflib/extras/msvcdeps.py
++++ third_party/waf/waflib/extras/msvcdeps.py
+@@ -150,11 +150,25 @@ def scan(self):
+ def sig_implicit_deps(self):
+ if self.env.CC_NAME not in supported_compilers:
+ return super(self.derived_msvcdeps, self).sig_implicit_deps()
++ bld = self.generator.bld
+
+ try:
+- return Task.Task.sig_implicit_deps(self)
+- except Errors.WafError:
+- return Utils.SIG_NIL
++ return self.compute_sig_implicit_deps()
++ except Errors.TaskNotReady:
++ raise ValueError("Please specify the build order precisely with msvcdeps (c/c++ tasks)")
++ except EnvironmentError:
++ # If a file is renamed, assume the dependencies are stale and must be recalculated
++ for x in bld.node_deps.get(self.uid(), []):
++ if not x.is_bld() and not x.exists():
++ try:
++ del x.parent.children[x.name]
++ except KeyError:
++ pass
++
++ key = self.uid()
++ bld.node_deps[key] = []
++ bld.raw_deps[key] = []
++ return Utils.SIG_NIL
+
+ def exec_command(self, cmd, **kw):
+ if self.env.CC_NAME not in supported_compilers:
+@@ -211,11 +225,14 @@ def exec_command(self, cmd, **kw):
+ # get one from the exception object
+ ret = getattr(e, 'returncode', 1)
+
++ Logs.debug('msvcdeps: Running for: %s' % self.inputs[0])
+ for line in raw_out.splitlines():
+ if line.startswith(INCLUDE_PATTERN):
+- inc_path = line[len(INCLUDE_PATTERN):].strip()
++ # Only strip whitespace after log to preserve
++ # dependency structure in debug output
++ inc_path = line[len(INCLUDE_PATTERN):]
+ Logs.debug('msvcdeps: Regex matched %s', inc_path)
+- self.msvcdeps_paths.append(inc_path)
++ self.msvcdeps_paths.append(inc_path.strip())
+ else:
+ out.append(line)
+
+diff --git third_party/waf/waflib/extras/pch.py third_party/waf/waflib/extras/pch.py
+index 103e752838c..b44c7a2e8fd 100644
+--- third_party/waf/waflib/extras/pch.py
++++ third_party/waf/waflib/extras/pch.py
+@@ -90,7 +90,7 @@ def apply_pch(self):
+
+ if getattr(self, 'name', None):
+ try:
+- task = self.bld.pch_tasks["%s.%s" % (self.name, self.idx)]
++ task = self.bld.pch_tasks[self.name]
+ self.bld.fatal("Duplicated 'pch' task with name %r" % "%s.%s" % (self.name, self.idx))
+ except KeyError:
+ pass
+@@ -104,7 +104,7 @@ def apply_pch(self):
+
+ self.pch_task = task
+ if getattr(self, 'name', None):
+- self.bld.pch_tasks["%s.%s" % (self.name, self.idx)] = task
++ self.bld.pch_tasks[self.name] = task
+
+ @TaskGen.feature('cxx')
+ @TaskGen.after_method('process_source', 'propagate_uselib_vars')
+diff --git third_party/waf/waflib/extras/sphinx.py third_party/waf/waflib/extras/sphinx.py
+index ce11110e634..71d1028393b 100644
+--- third_party/waf/waflib/extras/sphinx.py
++++ third_party/waf/waflib/extras/sphinx.py
+@@ -20,7 +20,7 @@ def build(bld):
+
+ from waflib.Node import Node
+ from waflib import Utils
+-from waflib.Task import Task
++from waflib import Task
+ from waflib.TaskGen import feature, after_method
+
+
+@@ -55,13 +55,9 @@ def build_sphinx(self):
+ sphinx_build_task.set_outputs(self.path.get_bld())
+
+ # the sphinx-build results are in <build + output_format> directory
+- sphinx_output_directory = self.path.get_bld().make_node(self.env.SPHINX_OUTPUT_FORMAT)
+- sphinx_output_directory.mkdir()
++ self.sphinx_output_directory = self.path.get_bld().make_node(self.env.SPHINX_OUTPUT_FORMAT)
++ self.sphinx_output_directory.mkdir()
+ Utils.def_attrs(self, install_path=get_install_path(self))
+- self.add_install_files(install_to=self.install_path,
+- install_from=sphinx_output_directory.ant_glob('**/*'),
+- cwd=sphinx_output_directory,
+- relative_trick=True)
+
+
+ def get_install_path(tg):
+@@ -73,9 +69,37 @@ def get_install_path(tg):
+ return tg.env.DOCDIR
+
+
+-class SphinxBuildingTask(Task):
++class SphinxBuildingTask(Task.Task):
+ color = 'BOLD'
+ run_str = '${SPHINX_BUILD} -M ${SPHINX_OUTPUT_FORMAT} ${SRC} ${TGT} ${SPHINX_OPTIONS}'
+
+ def keyword(self):
+ return 'Compiling (%s)' % self.env.SPHINX_OUTPUT_FORMAT
++
++ def runnable_status(self):
++
++ for x in self.run_after:
++ if not x.hasrun:
++ return Task.ASK_LATER
++
++ self.signature()
++ ret = Task.Task.runnable_status(self)
++ if ret == Task.SKIP_ME:
++ # in case the files were removed
++ self.add_install()
++ return ret
++
++
++ def post_run(self):
++ self.add_install()
++ return Task.Task.post_run(self)
++
++
++ def add_install(self):
++ nodes = self.generator.sphinx_output_directory.ant_glob('**/*', quiet=True)
++ self.outputs += nodes
++ self.generator.add_install_files(install_to=self.generator.install_path,
++ install_from=nodes,
++ postpone=False,
++ cwd=self.generator.sphinx_output_directory,
++ relative_trick=True)
+diff --git third_party/waf/waflib/extras/wafcache.py third_party/waf/waflib/extras/wafcache.py
+index 8b9567faf14..088fd0d098d 100644
+--- third_party/waf/waflib/extras/wafcache.py
++++ third_party/waf/waflib/extras/wafcache.py
+@@ -16,10 +16,19 @@ The following environment variables may be set:
+ - URL to a cache server, for example:
+ export WAFCACHE=http://localhost:8080/files/
+ in that case, GET/POST requests are made to urls of the form
+- http://localhost:8080/files/000000000/0 (cache management is then up to the server)
+- - GCS or S3 bucket
+- gs://my-bucket/
+- s3://my-bucket/
++ http://localhost:8080/files/000000000/0 (cache management is delegated to the server)
++ - GCS, S3 or MINIO bucket
++ gs://my-bucket/ (uses gsutil command line tool or WAFCACHE_CMD)
++ s3://my-bucket/ (uses aws command line tool or WAFCACHE_CMD)
++ minio://my-bucket/ (uses mc command line tool or WAFCACHE_CMD)
++* WAFCACHE_CMD: bucket upload/download command, for example:
++ WAFCACHE_CMD="gsutil cp %{SRC} %{TGT}"
++ Note that the WAFCACHE bucket value is used for the source or destination
++ depending on the operation (upload or download). For example, with:
++ WAFCACHE="gs://mybucket/"
++ the following commands may be run:
++ gsutil cp build/myprogram gs://mybucket/aa/aaaaa/1
++ gsutil cp gs://mybucket/bb/bbbbb/2 build/somefile
+ * WAFCACHE_NO_PUSH: if set, disables pushing to the cache
+ * WAFCACHE_VERBOSITY: if set, displays more detailed cache operations
+
+@@ -30,6 +39,7 @@ File cache specific options:
+ * WAFCACHE_EVICT_MAX_BYTES: maximum amount of cache size in bytes (10GB)
+ * WAFCACHE_EVICT_INTERVAL_MINUTES: minimum time interval to try
+ and trim the cache (3 minutess)
++
+ Usage::
+
+ def build(bld):
+@@ -41,7 +51,7 @@ To troubleshoot::
+ waf clean build --zones=wafcache
+ """
+
+-import atexit, base64, errno, fcntl, getpass, os, shutil, sys, time, traceback, urllib3
++import atexit, base64, errno, fcntl, getpass, os, re, shutil, sys, time, traceback, urllib3, shlex
+ try:
+ import subprocess32 as subprocess
+ except ImportError:
+@@ -53,6 +63,7 @@ if not os.path.isdir(base_cache):
+ default_wafcache_dir = os.path.join(base_cache, 'wafcache_' + getpass.getuser())
+
+ CACHE_DIR = os.environ.get('WAFCACHE', default_wafcache_dir)
++WAFCACHE_CMD = os.environ.get('WAFCACHE_CMD')
+ TRIM_MAX_FOLDERS = int(os.environ.get('WAFCACHE_TRIM_MAX_FOLDER', 1000000))
+ EVICT_INTERVAL_MINUTES = int(os.environ.get('WAFCACHE_EVICT_INTERVAL_MINUTES', 3))
+ EVICT_MAX_BYTES = int(os.environ.get('WAFCACHE_EVICT_MAX_BYTES', 10**10))
+@@ -60,6 +71,8 @@ WAFCACHE_NO_PUSH = 1 if os.environ.get('WAFCACHE_NO_PUSH') else 0
+ WAFCACHE_VERBOSITY = 1 if os.environ.get('WAFCACHE_VERBOSITY') else 0
+ OK = "ok"
+
++re_waf_cmd = re.compile('(?P<src>%{SRC})|(?P<tgt>%{TGT})')
++
+ try:
+ import cPickle
+ except ImportError:
+@@ -233,8 +246,9 @@ def build(bld):
+ # already called once
+ return
+
+- for x in range(bld.jobs):
+- process_pool.append(get_process())
++ # pre-allocation
++ processes = [get_process() for x in range(bld.jobs)]
++ process_pool.extend(processes)
+
+ Task.Task.can_retrieve_cache = can_retrieve_cache
+ Task.Task.put_files_cache = put_files_cache
+@@ -449,10 +463,20 @@ class fcache(object):
+
+ class bucket_cache(object):
+ def bucket_copy(self, source, target):
+- if CACHE_DIR.startswith('s3://'):
++ if WAFCACHE_CMD:
++ def replacer(match):
++ if match.group('src'):
++ return source
++ elif match.group('tgt'):
++ return target
++ cmd = [re_waf_cmd.sub(replacer, x) for x in shlex.split(WAFCACHE_CMD)]
++ elif CACHE_DIR.startswith('s3://'):
+ cmd = ['aws', 's3', 'cp', source, target]
+- else:
++ elif CACHE_DIR.startswith('gs://'):
+ cmd = ['gsutil', 'cp', source, target]
++ else:
++ cmd = ['mc', 'cp', source, target]
++
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ out, err = proc.communicate()
+ if proc.returncode:
+@@ -510,7 +534,9 @@ def loop(service):
+ sys.stdout.flush()
+
+ if __name__ == '__main__':
+- if CACHE_DIR.startswith('s3://') or CACHE_DIR.startswith('gs://'):
++ if CACHE_DIR.startswith('s3://') or CACHE_DIR.startswith('gs://') or CACHE_DIR.startswith('minio://'):
++ if CACHE_DIR.startswith('minio://'):
++ CACHE_DIR = CACHE_DIR[8:] # minio doesn't need the protocol part, uses config aliases
+ service = bucket_cache()
+ elif CACHE_DIR.startswith('http'):
+ service = netcache()
+diff --git third_party/waf/waflib/extras/xcode6.py third_party/waf/waflib/extras/xcode6.py
+index 91bbff181ec..c5b309120c9 100644
+--- third_party/waf/waflib/extras/xcode6.py
++++ third_party/waf/waflib/extras/xcode6.py
+@@ -99,7 +99,7 @@ env.PROJ_CONFIGURATION = {
+ ...
+ }
+ 'Release': {
+- 'ARCHS' x86_64'
++ 'ARCHS': x86_64'
+ ...
+ }
+ }
+@@ -163,12 +163,12 @@ class XCodeNode(object):
+ result = result + "\t\t}"
+ return result
+ elif isinstance(value, str):
+- return "\"%s\"" % value
++ return '"%s"' % value.replace('"', '\\\\\\"')
+ elif isinstance(value, list):
+ result = "(\n"
+ for i in value:
+- result = result + "\t\t\t%s,\n" % self.tostring(i)
+- result = result + "\t\t)"
++ result = result + "\t\t\t\t%s,\n" % self.tostring(i)
++ result = result + "\t\t\t)"
+ return result
+ elif isinstance(value, XCodeNode):
+ return value._id
+@@ -565,13 +565,13 @@ def process_xcode(self):
+ # Override target specific build settings
+ bldsettings = {
+ 'HEADER_SEARCH_PATHS': ['$(inherited)'] + self.env['INCPATHS'],
+- 'LIBRARY_SEARCH_PATHS': ['$(inherited)'] + Utils.to_list(self.env.LIBPATH) + Utils.to_list(self.env.STLIBPATH) + Utils.to_list(self.env.LIBDIR) ,
++ 'LIBRARY_SEARCH_PATHS': ['$(inherited)'] + Utils.to_list(self.env.LIBPATH) + Utils.to_list(self.env.STLIBPATH) + Utils.to_list(self.env.LIBDIR),
+ 'FRAMEWORK_SEARCH_PATHS': ['$(inherited)'] + Utils.to_list(self.env.FRAMEWORKPATH),
+- 'OTHER_LDFLAGS': libs + ' ' + frameworks,
+- 'OTHER_LIBTOOLFLAGS': bld.env['LINKFLAGS'],
++ 'OTHER_LDFLAGS': libs + ' ' + frameworks + ' ' + ' '.join(bld.env['LINKFLAGS']),
+ 'OTHER_CPLUSPLUSFLAGS': Utils.to_list(self.env['CXXFLAGS']),
+ 'OTHER_CFLAGS': Utils.to_list(self.env['CFLAGS']),
+- 'INSTALL_PATH': []
++ 'INSTALL_PATH': [],
++ 'GCC_PREPROCESSOR_DEFINITIONS': self.env['DEFINES']
+ }
+
+ # Install path
+@@ -591,7 +591,7 @@ def process_xcode(self):
+
+ # The keys represents different build configuration, e.g. Debug, Release and so on..
+ # Insert our generated build settings to all configuration names
+- keys = set(settings.keys() + bld.env.PROJ_CONFIGURATION.keys())
++ keys = set(settings.keys()) | set(bld.env.PROJ_CONFIGURATION.keys())
+ for k in keys:
+ if k in settings:
+ settings[k].update(bldsettings)
+--
+2.37.3
+
diff --git a/net/samba413/files/patch-waf-2.0.22 b/net/samba413/files/patch-waf-2.0.22
new file mode 100644
index 000000000000..db3c8edff8d3
--- /dev/null
+++ b/net/samba413/files/patch-waf-2.0.22
@@ -0,0 +1,596 @@
+From 59ed09928541d40df72592419247add608a54aca Mon Sep 17 00:00:00 2001
+From: Andreas Schneider <asn@samba.org>
+Date: Wed, 25 Aug 2021 15:34:58 +0200
+Subject: [PATCH] third_party: Update waf to version 2.0.22
+
+New in waf 2.0.22
+
+* Fix stdin propagation with faulty vcvarsall scripts #2315
+* Enable mixing Unix-style paths with destdir on Windows platforms #2337
+* Fix shell escaping unit test parameters #2314
+* Improve extras/clang_compilation_database and extras/swig compatibility #2336
+* Propagate C++ flags to the Cuda compiler in extras/cuda #2311
+* Fix detection of Qt 5.0.0 (preparation for Qt6) #2331
+* Enable Haxe processing #2308
+* Fix regression in MACOSX_DEPLOYMENT_TARGET caused by distutils #2330
+* Fix extras/wafcache concurrent trimming issues #2312
+* Fix extras/wafcache symlink handling #2327
+
+The import was done like this:
+
+./third_party/waf/update.sh
+
+Then changing buildtools/bin/waf and buildtools/wafsamba/wafsamba.py
+by hand.
+
+Pair-Programmed-With: Stefan Metzmacher <metze@samba.org>
+
+Signed-off-by: Andreas Schneider <asn@samba.org>
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Reviewed-by: Andrew Bartlett <abartlet@samba.org>
+
+Autobuild-User(master): Andrew Bartlett <abartlet@samba.org>
+Autobuild-Date(master): Thu Sep 2 21:22:17 UTC 2021 on sn-devel-184
+---
+ buildtools/bin/waf | 2 +-
+ buildtools/wafsamba/wafsamba.py | 2 +-
+ third_party/waf/waflib/Build.py | 4 +-
+ third_party/waf/waflib/Context.py | 6 +-
+ third_party/waf/waflib/Tools/msvc.py | 2 +-
+ third_party/waf/waflib/Tools/python.py | 2 +-
+ third_party/waf/waflib/Tools/qt5.py | 6 +-
+ third_party/waf/waflib/Tools/waf_unit_test.py | 2 +-
+ third_party/waf/waflib/Utils.py | 15 +-
+ .../extras/clang_compilation_database.py | 28 ++--
+ third_party/waf/waflib/extras/haxe.py | 131 ++++++++++++++++++
+ third_party/waf/waflib/extras/wafcache.py | 59 ++++++--
+ 12 files changed, 215 insertions(+), 44 deletions(-)
+ create mode 100644 third_party/waf/waflib/extras/haxe.py
+
+diff --git buildtools/bin/waf buildtools/bin/waf
+index 041450fc131..b0ccb09a877 100755
+--- buildtools/bin/waf
++++ buildtools/bin/waf
+@@ -32,7 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
+
+ import os, sys, inspect
+
+-VERSION="2.0.21"
++VERSION="2.0.22"
+ REVISION="x"
+ GIT="x"
+ INSTALL="x"
+diff --git buildtools/wafsamba/wafsamba.py buildtools/wafsamba/wafsamba.py
+index 4fe9daf160e..dee007bf84e 100644
+--- buildtools/wafsamba/wafsamba.py
++++ buildtools/wafsamba/wafsamba.py
+@@ -38,7 +38,7 @@ LIB_PATH="shared"
+
+ os.environ['PYTHONUNBUFFERED'] = '1'
+
+-if Context.HEXVERSION not in (0x2001500,):
++if Context.HEXVERSION not in (0x2001600,):
+ Logs.error('''
+ Please use the version of waf that comes with Samba, not
+ a system installed version. See http://wiki.samba.org/index.php/Waf
+diff --git third_party/waf/waflib/Build.py third_party/waf/waflib/Build.py
+index 52837618577..b49dd8302b1 100644
+--- third_party/waf/waflib/Build.py
++++ third_party/waf/waflib/Build.py
+@@ -1066,9 +1066,9 @@ class inst(Task.Task):
+ else:
+ dest = os.path.normpath(Utils.subst_vars(self.install_to, self.env))
+ if not os.path.isabs(dest):
+- dest = os.path.join(self.env.PREFIX, dest)
++ dest = os.path.join(self.env.PREFIX, dest)
+ if destdir and Options.options.destdir:
+- dest = os.path.join(Options.options.destdir, os.path.splitdrive(dest)[1].lstrip(os.sep))
++ dest = Options.options.destdir.rstrip(os.sep) + os.sep + os.path.splitdrive(dest)[1].lstrip(os.sep)
+ return dest
+
+ def copy_fun(self, src, tgt):
+diff --git third_party/waf/waflib/Context.py third_party/waf/waflib/Context.py
+index 0ce9df6e91f..07ee1201f03 100644
+--- third_party/waf/waflib/Context.py
++++ third_party/waf/waflib/Context.py
+@@ -18,13 +18,13 @@ else:
+ import imp
+
+ # the following 3 constants are updated on each new release (do not touch)
+-HEXVERSION=0x2001500
++HEXVERSION=0x2001600
+ """Constant updated on new releases"""
+
+-WAFVERSION="2.0.21"
++WAFVERSION="2.0.22"
+ """Constant updated on new releases"""
+
+-WAFREVISION="edde20a6425a5c3eb6b47d5f3f5c4fbc93fed5f4"
++WAFREVISION="816d5bc48ba2abc4ac22f2b44d94d322bf992b9c"
+ """Git revision when the waf version is updated"""
+
+ WAFNAME="waf"
+diff --git third_party/waf/waflib/Tools/msvc.py third_party/waf/waflib/Tools/msvc.py
+index 37233be8242..0c4703aaee9 100644
+--- third_party/waf/waflib/Tools/msvc.py
++++ third_party/waf/waflib/Tools/msvc.py
+@@ -193,7 +193,7 @@ echo PATH=%%PATH%%
+ echo INCLUDE=%%INCLUDE%%
+ echo LIB=%%LIB%%;%%LIBPATH%%
+ """ % (vcvars,target))
+- sout = conf.cmd_and_log(['cmd.exe', '/E:on', '/V:on', '/C', batfile.abspath()])
++ sout = conf.cmd_and_log(['cmd.exe', '/E:on', '/V:on', '/C', batfile.abspath()], stdin=getattr(Utils.subprocess, 'DEVNULL', None))
+ lines = sout.splitlines()
+
+ if not lines[0]:
+diff --git third_party/waf/waflib/Tools/python.py third_party/waf/waflib/Tools/python.py
+index b1c8dd01285..07442561dff 100644
+--- third_party/waf/waflib/Tools/python.py
++++ third_party/waf/waflib/Tools/python.py
+@@ -327,7 +327,7 @@ def check_python_headers(conf, features='pyembed pyext'):
+ dct = dict(zip(v, lst))
+ x = 'MACOSX_DEPLOYMENT_TARGET'
+ if dct[x]:
+- env[x] = conf.environ[x] = dct[x]
++ env[x] = conf.environ[x] = str(dct[x])
+ env.pyext_PATTERN = '%s' + dct['SO'] # not a mistake
+
+
+diff --git third_party/waf/waflib/Tools/qt5.py third_party/waf/waflib/Tools/qt5.py
+index cff2028174f..82c83e18c8a 100644
+--- third_party/waf/waflib/Tools/qt5.py
++++ third_party/waf/waflib/Tools/qt5.py
+@@ -566,7 +566,7 @@ def find_qt5_binaries(self):
+ # at the end, try to find qmake in the paths given
+ # keep the one with the highest version
+ cand = None
+- prev_ver = ['5', '0', '0']
++ prev_ver = ['0', '0', '0']
+ for qmk in ('qmake-qt5', 'qmake5', 'qmake'):
+ try:
+ qmake = self.find_program(qmk, path_list=paths)
+@@ -580,7 +580,7 @@ def find_qt5_binaries(self):
+ else:
+ if version:
+ new_ver = version.split('.')
+- if new_ver > prev_ver:
++ if new_ver[0] == '5' and new_ver > prev_ver:
+ cand = qmake
+ prev_ver = new_ver
+
+@@ -783,7 +783,7 @@ def set_qt5_libs_to_check(self):
+ pat = self.env.cxxstlib_PATTERN
+ if Utils.unversioned_sys_platform() == 'darwin':
+ pat = r"%s\.framework"
+- re_qt = re.compile(pat%'Qt5?(?P<name>.*)'+'$')
++ re_qt = re.compile(pat % 'Qt5?(?P<name>\\D+)' + '$')
+ for x in dirlst:
+ m = re_qt.match(x)
+ if m:
+diff --git third_party/waf/waflib/Tools/waf_unit_test.py third_party/waf/waflib/Tools/waf_unit_test.py
+index dc66fe9c184..8cff89bdeb9 100644
+--- third_party/waf/waflib/Tools/waf_unit_test.py
++++ third_party/waf/waflib/Tools/waf_unit_test.py
+@@ -206,7 +206,7 @@ class utest(Task.Task):
+ self.ut_exec = getattr(self.generator, 'ut_exec', [self.inputs[0].abspath()])
+ ut_cmd = getattr(self.generator, 'ut_cmd', False)
+ if ut_cmd:
+- self.ut_exec = shlex.split(ut_cmd % ' '.join(self.ut_exec))
++ self.ut_exec = shlex.split(ut_cmd % Utils.shell_escape(self.ut_exec))
+
+ return self.exec_command(self.ut_exec)
+
+diff --git third_party/waf/waflib/Utils.py third_party/waf/waflib/Utils.py
+index fc64fa05154..669490ca908 100644
+--- third_party/waf/waflib/Utils.py
++++ third_party/waf/waflib/Utils.py
+@@ -11,7 +11,7 @@ through Python versions 2.5 to 3.X and across different platforms (win32, linux,
+
+ from __future__ import with_statement
+
+-import atexit, os, sys, errno, inspect, re, datetime, platform, base64, signal, functools, time
++import atexit, os, sys, errno, inspect, re, datetime, platform, base64, signal, functools, time, shlex
+
+ try:
+ import cPickle
+@@ -577,10 +577,13 @@ def quote_define_name(s):
+ fu = fu.upper()
+ return fu
+
+-re_sh = re.compile('\\s|\'|"')
+-"""
+-Regexp used for shell_escape below
+-"""
++# shlex.quote didn't exist until python 3.3. Prior to that it was a non-documented
++# function in pipes.
++try:
++ shell_quote = shlex.quote
++except AttributeError:
++ import pipes
++ shell_quote = pipes.quote
+
+ def shell_escape(cmd):
+ """
+@@ -589,7 +592,7 @@ def shell_escape(cmd):
+ """
+ if isinstance(cmd, str):
+ return cmd
+- return ' '.join(repr(x) if re_sh.search(x) else x for x in cmd)
++ return ' '.join(shell_quote(x) for x in cmd)
+
+ def h_list(lst):
+ """
+diff --git third_party/waf/waflib/extras/clang_compilation_database.py third_party/waf/waflib/extras/clang_compilation_database.py
+index ff71f22ecfd..17f66949376 100644
+--- third_party/waf/waflib/extras/clang_compilation_database.py
++++ third_party/waf/waflib/extras/clang_compilation_database.py
+@@ -29,22 +29,9 @@ from waflib import Logs, TaskGen, Task, Build, Scripting
+
+ Task.Task.keep_last_cmd = True
+
+-@TaskGen.feature('c', 'cxx')
+-@TaskGen.after_method('process_use')
+-def collect_compilation_db_tasks(self):
+- "Add a compilation database entry for compiled tasks"
+- if not isinstance(self.bld, ClangDbContext):
+- return
+-
+- tup = tuple(y for y in [Task.classes.get(x) for x in ('c', 'cxx')] if y)
+- for task in getattr(self, 'compiled_tasks', []):
+- if isinstance(task, tup):
+- self.bld.clang_compilation_database_tasks.append(task)
+-
+ class ClangDbContext(Build.BuildContext):
+ '''generates compile_commands.json by request'''
+ cmd = 'clangdb'
+- clang_compilation_database_tasks = []
+
+ def write_compilation_database(self):
+ """
+@@ -78,6 +65,8 @@ class ClangDbContext(Build.BuildContext):
+ Build dry run
+ """
+ self.restore()
++ self.cur_tasks = []
++ self.clang_compilation_database_tasks = []
+
+ if not self.all_envs:
+ self.load_envs()
+@@ -103,8 +92,21 @@ class ClangDbContext(Build.BuildContext):
+ lst = [tg]
+ else: lst = tg.tasks
+ for tsk in lst:
++ if tsk.__class__.__name__ == "swig":
++ tsk.runnable_status()
++ if hasattr(tsk, 'more_tasks'):
++ lst.extend(tsk.more_tasks)
++ # Not all dynamic tasks can be processed, in some cases
++ # one may have to call the method "run()" like this:
++ #elif tsk.__class__.__name__ == 'src2c':
++ # tsk.run()
++ # if hasattr(tsk, 'more_tasks'):
++ # lst.extend(tsk.more_tasks)
++
+ tup = tuple(y for y in [Task.classes.get(x) for x in ('c', 'cxx')] if y)
+ if isinstance(tsk, tup):
++ self.clang_compilation_database_tasks.append(tsk)
++ tsk.nocache = True
+ old_exec = tsk.exec_command
+ tsk.exec_command = exec_command
+ tsk.run()
+diff --git third_party/waf/waflib/extras/haxe.py third_party/waf/waflib/extras/haxe.py
+new file mode 100644
+index 00000000000..cb3ba6a949c
+--- /dev/null
++++ third_party/waf/waflib/extras/haxe.py
+@@ -0,0 +1,131 @@
++import os, re
++from waflib import Utils, Task, Errors
++from waflib.TaskGen import extension, taskgen_method, feature
++from waflib.Configure import conf
++
++@conf
++def libname_haxe(self, libname):
++ return libname
++
++@conf
++def check_lib_haxe(self, libname, uselib_store=None):
++ haxe_libs = [node.name for node in self.root.find_node('haxe_libraries').ant_glob()]
++ changed = False
++ self.start_msg('Checking for library %s' % libname)
++ if libname + '.hxml' in haxe_libs:
++ self.end_msg('yes')
++ else:
++ changed = True
++ try:
++ cmd = self.env.LIX + ['+lib', libname]
++ res = self.cmd_and_log(cmd)
++ if (res):
++ raise Errors.WafError(res)
++ else:
++ self.end_msg('downloaded', color = 'YELLOW')
++ except Errors.WafError as e:
++ self.end_msg('no', color = 'RED')
++ self.fatal('Getting %s has failed' % libname)
++
++ postfix = uselib_store if uselib_store else libname.upper()
++ self.env['LIB_' + postfix] += [self.libname_haxe(libname)]
++ return changed
++
++@conf
++def check_libs_haxe(self, libnames, uselib_store=None):
++ changed = False
++ for libname in Utils.to_list(libnames):
++ if self.check_lib_haxe(libname, uselib_store):
++ changed = True
++ return changed
++
++@conf
++def ensure_lix_pkg(self, *k, **kw):
++ if kw.get('compiler') == 'hx':
++ if isinstance(kw.get('libs'), list) and len(kw.get('libs')):
++ changed = self.check_libs_haxe(kw.get('libs'), kw.get('uselib_store'))
++ if changed:
++ try:
++ cmd = self.env.LIX + ['download']
++ res = self.cmd_and_log(cmd)
++ if (res):
++ raise Errors.WafError(res)
++ except Errors.WafError as e:
++ self.fatal('lix download has failed')
++ else:
++ self.check_lib_haxe(kw.get('lib'), kw.get('uselib_store'))
++
++@conf
++def haxe(bld, *k, **kw):
++ task_gen = bld(*k, **kw)
++
++class haxe(Task.Task):
++ vars = ['HAXE', 'HAXE_VERSION', 'HAXEFLAGS']
++ ext_out = ['.hl', '.c', '.h']
++
++ def run(self):
++ cmd = self.env.HAXE + self.env.HAXEFLAGS
++ return self.exec_command(cmd, stdout = open(os.devnull, 'w'))
++
++@taskgen_method
++def init_haxe_task(self, node):
++ def addflags(flags):
++ self.env.append_value('HAXEFLAGS', flags)
++
++ if node.suffix() == '.hxml':
++ addflags(self.path.abspath() + '/' + node.name)
++ else:
++ addflags(['-main', node.name])
++ addflags(['-hl', self.path.get_bld().make_node(self.target).abspath()])
++ addflags(['-cp', self.path.abspath()])
++ addflags(['-D', 'resourcesPath=%s' % getattr(self, 'res', '')])
++ if hasattr(self, 'use'):
++ for dep in self.use:
++ if self.env['LIB_' + dep]:
++ for lib in self.env['LIB_' + dep]: addflags(['-lib', lib])
++
++@extension('.hx', '.hxml')
++def haxe_file(self, node):
++ if len(self.source) > 1:
++ self.bld.fatal('Use separate task generators for multiple files')
++
++ try:
++ haxetask = self.haxetask
++ except AttributeError:
++ haxetask = self.haxetask = self.create_task('haxe')
++ self.init_haxe_task(node)
++
++ haxetask.inputs.append(node)
++ haxetask.outputs.append(self.path.get_bld().make_node(self.target))
++
++@conf
++def find_haxe(self, min_version):
++ npx = self.env.NPX = self.find_program('npx')
++ self.env.LIX = npx + ['lix']
++ npx_haxe = self.env.HAXE = npx + ['haxe']
++ try:
++ output = self.cmd_and_log(npx_haxe + ['-version'])
++ except Errors.WafError:
++ haxe_version = None
++ else:
++ ver = re.search(r'\d+.\d+.\d+', output).group().split('.')
++ haxe_version = tuple([int(x) for x in ver])
++
++ self.msg('Checking for haxe version',
++ haxe_version, haxe_version and haxe_version >= min_version)
++ if npx_haxe and haxe_version < min_version:
++ self.fatal('haxe version %r is too old, need >= %r' % (haxe_version, min_version))
++
++ self.env.HAXE_VERSION = haxe_version
++ return npx_haxe
++
++@conf
++def check_haxe(self, min_version=(4,1,4)):
++ if self.env.HAXE_MINVER:
++ min_version = self.env.HAXE_MINVER
++ find_haxe(self, min_version)
++
++def configure(self):
++ self.env.HAXEFLAGS = []
++ self.check_haxe()
++ self.add_os_flags('HAXEFLAGS', dup = False)
+diff --git third_party/waf/waflib/extras/wafcache.py third_party/waf/waflib/extras/wafcache.py
+index 088fd0d098d..cc23fcd6673 100644
+--- third_party/waf/waflib/extras/wafcache.py
++++ third_party/waf/waflib/extras/wafcache.py
+@@ -31,6 +31,7 @@ The following environment variables may be set:
+ gsutil cp gs://mybucket/bb/bbbbb/2 build/somefile
+ * WAFCACHE_NO_PUSH: if set, disables pushing to the cache
+ * WAFCACHE_VERBOSITY: if set, displays more detailed cache operations
++* WAFCACHE_STATS: if set, displays cache usage statistics on exit
+
+ File cache specific options:
+ Files are copied using hard links by default; if the cache is located
+@@ -69,6 +70,7 @@ EVICT_INTERVAL_MINUTES = int(os.environ.get('WAFCACHE_EVICT_INTERVAL_MINUTES', 3
+ EVICT_MAX_BYTES = int(os.environ.get('WAFCACHE_EVICT_MAX_BYTES', 10**10))
+ WAFCACHE_NO_PUSH = 1 if os.environ.get('WAFCACHE_NO_PUSH') else 0
+ WAFCACHE_VERBOSITY = 1 if os.environ.get('WAFCACHE_VERBOSITY') else 0
++WAFCACHE_STATS = 1 if os.environ.get('WAFCACHE_STATS') else 0
+ OK = "ok"
+
+ re_waf_cmd = re.compile('(?P<src>%{SRC})|(?P<tgt>%{TGT})')
+@@ -93,6 +95,9 @@ def can_retrieve_cache(self):
+ sig = self.signature()
+ ssig = Utils.to_hex(self.uid() + sig)
+
++ if WAFCACHE_STATS:
++ self.generator.bld.cache_reqs += 1
++
+ files_to = [node.abspath() for node in self.outputs]
+ err = cache_command(ssig, [], files_to)
+ if err.startswith(OK):
+@@ -100,6 +105,8 @@ def can_retrieve_cache(self):
+ Logs.pprint('CYAN', ' Fetched %r from cache' % files_to)
+ else:
+ Logs.debug('wafcache: fetched %r from cache', files_to)
++ if WAFCACHE_STATS:
++ self.generator.bld.cache_hits += 1
+ else:
+ if WAFCACHE_VERBOSITY:
+ Logs.pprint('YELLOW', ' No cache entry %s' % files_to)
+@@ -117,11 +124,17 @@ def put_files_cache(self):
+ if WAFCACHE_NO_PUSH or getattr(self, 'cached', None) or not self.outputs:
+ return
+
++ files_from = []
++ for node in self.outputs:
++ path = node.abspath()
++ if not os.path.isfile(path):
++ return
++ files_from.append(path)
++
+ bld = self.generator.bld
+ sig = self.signature()
+ ssig = Utils.to_hex(self.uid() + sig)
+
+- files_from = [node.abspath() for node in self.outputs]
+ err = cache_command(ssig, files_from, [])
+
+ if err.startswith(OK):
+@@ -129,6 +142,8 @@ def put_files_cache(self):
+ Logs.pprint('CYAN', ' Successfully uploaded %s to cache' % files_from)
+ else:
+ Logs.debug('wafcache: Successfully uploaded %r to cache', files_from)
++ if WAFCACHE_STATS:
++ self.generator.bld.cache_puts += 1
+ else:
+ if WAFCACHE_VERBOSITY:
+ Logs.pprint('RED', ' Error caching step results %s: %s' % (files_from, err))
+@@ -193,6 +208,10 @@ def make_cached(cls):
+ if getattr(cls, 'nocache', None) or getattr(cls, 'has_cache', False):
+ return
+
++ full_name = "%s.%s" % (cls.__module__, cls.__name__)
++ if full_name in ('waflib.Tools.ccroot.vnum', 'waflib.Build.inst'):
++ return
++
+ m1 = getattr(cls, 'run', None)
+ def run(self):
+ if getattr(self, 'nocache', False):
+@@ -208,9 +227,6 @@ def make_cached(cls):
+ return m2(self)
+ ret = m2(self)
+ self.put_files_cache()
+- if hasattr(self, 'chmod'):
+- for node in self.outputs:
+- os.chmod(node.abspath(), self.chmod)
+ return ret
+ cls.post_run = post_run
+ cls.has_cache = True
+@@ -257,6 +273,19 @@ def build(bld):
+ for x in reversed(list(Task.classes.values())):
+ make_cached(x)
+
++ if WAFCACHE_STATS:
++ # Init counter for statistics and hook to print results at the end
++ bld.cache_reqs = bld.cache_hits = bld.cache_puts = 0
++
++ def printstats(bld):
++ hit_ratio = 0
++ if bld.cache_reqs > 0:
++ hit_ratio = (bld.cache_hits / bld.cache_reqs) * 100
++ Logs.pprint('CYAN', ' wafcache stats: requests: %s, hits, %s, ratio: %.2f%%, writes %s' %
++ (bld.cache_reqs, bld.cache_hits, hit_ratio, bld.cache_puts) )
++
++ bld.add_post_fun(printstats)
++
+ def cache_command(sig, files_from, files_to):
+ """
+ Create a command for cache worker processes, returns a pickled
+@@ -320,7 +349,10 @@ def lru_trim():
+
+ size = 0
+ for fname in os.listdir(path):
+- size += os.lstat(os.path.join(path, fname)).st_size
++ try:
++ size += os.lstat(os.path.join(path, fname)).st_size
++ except OSError:
++ pass
+ lst.append((os.stat(path).st_mtime, size, path))
+
+ lst.sort(key=lambda x: x[0])
+@@ -331,7 +363,7 @@ def lru_trim():
+ _, tmp_size, path = lst.pop()
+ tot -= tmp_size
+
+- tmp = path + '.tmp'
++ tmp = path + '.remove'
+ try:
+ shutil.rmtree(tmp)
+ except OSError:
+@@ -339,12 +371,12 @@ def lru_trim():
+ try:
+ os.rename(path, tmp)
+ except OSError:
+- sys.stderr.write('Could not rename %r to %r' % (path, tmp))
++ sys.stderr.write('Could not rename %r to %r\n' % (path, tmp))
+ else:
+ try:
+ shutil.rmtree(tmp)
+ except OSError:
+- sys.stderr.write('Could not remove %r' % tmp)
++ sys.stderr.write('Could not remove %r\n' % tmp)
+ sys.stderr.write("Cache trimmed: %r bytes in %r folders left\n" % (tot, len(lst)))
+
+
+@@ -371,8 +403,8 @@ def lru_evict():
+ try:
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ except EnvironmentError:
+- sys.stderr.write('another process is running!\n')
+- pass
++ if WAFCACHE_VERBOSITY:
++ sys.stderr.write('wafcache: another cleaning process is running\n')
+ else:
+ # now dow the actual cleanup
+ lru_trim()
+@@ -443,7 +475,10 @@ class fcache(object):
+ else:
+ # attempt trimming if caching was successful:
+ # we may have things to trim!
+- lru_evict()
++ try:
++ lru_evict()
++ except Exception:
++ return traceback.format_exc()
+ return OK
+
+ def copy_from_cache(self, sig, files_from, files_to):
+@@ -481,7 +516,7 @@ class bucket_cache(object):
+ out, err = proc.communicate()
+ if proc.returncode:
+ raise OSError('Error copy %r to %r using: %r (exit %r):\n out:%s\n err:%s' % (
+- source, target, cmd, proc.returncode, out.decode(), err.decode()))
++ source, target, cmd, proc.returncode, out.decode(errors='replace'), err.decode(errors='replace')))
+
+ def copy_to_cache(self, sig, files_from, files_to):
+ try:
+--
+2.37.3
+
diff --git a/net/samba413/files/patch-waf-2.0.23 b/net/samba413/files/patch-waf-2.0.23
new file mode 100644
index 000000000000..36a70e32e8c3
--- /dev/null
+++ b/net/samba413/files/patch-waf-2.0.23
@@ -0,0 +1,877 @@
+From fb175576b698f43224dab815fd6c0763a12db2b2 Mon Sep 17 00:00:00 2001
+From: Andreas Schneider <asn@samba.org>
+Date: Thu, 17 Feb 2022 15:40:20 +0100
+Subject: [PATCH] third_party: Update waf to verison 2.0.23
+
+Signed-off-by: Andreas Schneider <asn@samba.org>
+Reviewed-by: Alexander Bokovoy <ab@samba.org>
+
+Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
+Autobuild-Date(master): Mon Feb 21 10:06:27 UTC 2022 on sn-devel-184
+---
+ buildtools/bin/waf | 3 +-
+ buildtools/wafsamba/wafsamba.py | 2 +-
+ third_party/waf/waflib/Context.py | 6 +-
+ third_party/waf/waflib/Runner.py | 4 +-
+ third_party/waf/waflib/TaskGen.py | 8 +-
+ third_party/waf/waflib/Tools/c_config.py | 1 +
+ third_party/waf/waflib/Tools/compiler_c.py | 25 +++---
+ third_party/waf/waflib/Tools/compiler_cxx.py | 25 +++---
+ third_party/waf/waflib/Tools/python.py | 7 +-
+ third_party/waf/waflib/Tools/qt5.py | 4 +-
+ third_party/waf/waflib/Tools/winres.py | 35 ++++++++
+ .../extras/clang_compilation_database.py | 2 +-
+ .../waf/waflib/extras/classic_runner.py | 68 +++++++++++++++
+ third_party/waf/waflib/extras/color_gcc.py | 2 +-
+ third_party/waf/waflib/extras/eclipse.py | 74 ++++++++++++++++-
+ third_party/waf/waflib/extras/gccdeps.py | 82 ++++++++++---------
+ third_party/waf/waflib/extras/msvcdeps.py | 54 ++++++++----
+ third_party/waf/waflib/extras/msvs.py | 6 +-
+ third_party/waf/waflib/extras/swig.py | 2 +-
+ third_party/waf/waflib/extras/wafcache.py | 26 +++---
+ third_party/waf/waflib/fixpy2.py | 2 +-
+ 21 files changed, 325 insertions(+), 113 deletions(-)
+ create mode 100644 third_party/waf/waflib/extras/classic_runner.py
+
+diff --git buildtools/bin/waf buildtools/bin/waf
+index b0ccb09a877..2001ccdbd8a 100755
+--- buildtools/bin/waf
++++ buildtools/bin/waf
+@@ -32,7 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
+
+ import os, sys, inspect
+
+-VERSION="2.0.22"
++VERSION="2.0.23"
+ REVISION="x"
+ GIT="x"
+ INSTALL="x"
+@@ -164,4 +164,3 @@ if __name__ == '__main__':
+
+ from waflib import Scripting
+ Scripting.waf_entry_point(cwd, VERSION, wafdir[0])
+-
+diff --git buildtools/wafsamba/wafsamba.py buildtools/wafsamba/wafsamba.py
+index 185ef3b73a2..710b82af663 100644
+--- buildtools/wafsamba/wafsamba.py
++++ buildtools/wafsamba/wafsamba.py
+@@ -38,7 +38,7 @@ LIB_PATH="shared"
+
+ os.environ['PYTHONUNBUFFERED'] = '1'
+
+-if Context.HEXVERSION not in (0x2001600,):
++if Context.HEXVERSION not in (0x2001700,):
+ Logs.error('''
+ Please use the version of waf that comes with Samba, not
+ a system installed version. See http://wiki.samba.org/index.php/Waf
+diff --git third_party/waf/waflib/Context.py third_party/waf/waflib/Context.py
+index 07ee1201f03..36d1ca74fef 100644
+--- third_party/waf/waflib/Context.py
++++ third_party/waf/waflib/Context.py
+@@ -18,13 +18,13 @@ else:
+ import imp
+
+ # the following 3 constants are updated on each new release (do not touch)
+-HEXVERSION=0x2001600
++HEXVERSION=0x2001700
+ """Constant updated on new releases"""
+
+-WAFVERSION="2.0.22"
++WAFVERSION="2.0.23"
+ """Constant updated on new releases"""
+
+-WAFREVISION="816d5bc48ba2abc4ac22f2b44d94d322bf992b9c"
++WAFREVISION="cc6b34cf555d354c34f554c41206134072588de7"
+ """Git revision when the waf version is updated"""
+
+ WAFNAME="waf"
+diff --git third_party/waf/waflib/Runner.py third_party/waf/waflib/Runner.py
+index 91d55479e20..350c86a22c0 100644
+--- third_party/waf/waflib/Runner.py
++++ third_party/waf/waflib/Runner.py
+@@ -71,7 +71,7 @@ class Consumer(Utils.threading.Thread):
+ """Task to execute"""
+ self.spawner = spawner
+ """Coordinator object"""
+- self.setDaemon(1)
++ self.daemon = True
+ self.start()
+ def run(self):
+ """
+@@ -98,7 +98,7 @@ class Spawner(Utils.threading.Thread):
+ """:py:class:`waflib.Runner.Parallel` producer instance"""
+ self.sem = Utils.threading.Semaphore(master.numjobs)
+ """Bounded semaphore that prevents spawning more than *n* concurrent consumers"""
+- self.setDaemon(1)
++ self.daemon = True
+ self.start()
+ def run(self):
+ """
+diff --git third_party/waf/waflib/TaskGen.py third_party/waf/waflib/TaskGen.py
+index f8f92bd57c1..89f63169910 100644
+--- third_party/waf/waflib/TaskGen.py
++++ third_party/waf/waflib/TaskGen.py
+@@ -631,12 +631,8 @@ def process_rule(self):
+ cls.scan = self.scan
+ elif has_deps:
+ def scan(self):
+- nodes = []
+- for x in self.generator.to_list(getattr(self.generator, 'deps', None)):
+- node = self.generator.path.find_resource(x)
+- if not node:
+- self.generator.bld.fatal('Could not find %r (was it declared?)' % x)
+- nodes.append(node)
++ deps = getattr(self.generator, 'deps', None)
++ nodes = self.generator.to_nodes(deps)
+ return [nodes, []]
+ cls.scan = scan
+
+diff --git third_party/waf/waflib/Tools/c_config.py third_party/waf/waflib/Tools/c_config.py
+index 03b6bf61bc0..f5ab19bf6ce 100644
+--- third_party/waf/waflib/Tools/c_config.py
++++ third_party/waf/waflib/Tools/c_config.py
+@@ -69,6 +69,7 @@ MACRO_TO_DEST_CPU = {
+ '__sh__' : 'sh',
+ '__xtensa__' : 'xtensa',
+ '__e2k__' : 'e2k',
++'__riscv' : 'riscv',
+ }
+
+ @conf
+diff --git third_party/waf/waflib/Tools/compiler_c.py third_party/waf/waflib/Tools/compiler_c.py
+index 931dc57efec..e033ce6c5c3 100644
+--- third_party/waf/waflib/Tools/compiler_c.py
++++ third_party/waf/waflib/Tools/compiler_c.py
+@@ -36,18 +36,19 @@ from waflib import Utils
+ from waflib.Logs import debug
+
+ c_compiler = {
+-'win32': ['msvc', 'gcc', 'clang'],
+-'cygwin': ['gcc', 'clang'],
+-'darwin': ['clang', 'gcc'],
+-'aix': ['xlc', 'gcc', 'clang'],
+-'linux': ['gcc', 'clang', 'icc'],
+-'sunos': ['suncc', 'gcc'],
+-'irix': ['gcc', 'irixcc'],
+-'hpux': ['gcc'],
+-'osf1V': ['gcc'],
+-'gnu': ['gcc', 'clang'],
+-'java': ['gcc', 'msvc', 'clang', 'icc'],
+-'default':['clang', 'gcc'],
++'win32': ['msvc', 'gcc', 'clang'],
++'cygwin': ['gcc', 'clang'],
++'darwin': ['clang', 'gcc'],
++'aix': ['xlc', 'gcc', 'clang'],
++'linux': ['gcc', 'clang', 'icc'],
++'sunos': ['suncc', 'gcc'],
++'irix': ['gcc', 'irixcc'],
++'hpux': ['gcc'],
++'osf1V': ['gcc'],
++'gnu': ['gcc', 'clang'],
++'java': ['gcc', 'msvc', 'clang', 'icc'],
++'gnukfreebsd': ['gcc', 'clang'],
++'default': ['clang', 'gcc'],
+ }
+ """
+ Dict mapping platform names to Waf tools finding specific C compilers::
+diff --git third_party/waf/waflib/Tools/compiler_cxx.py third_party/waf/waflib/Tools/compiler_cxx.py
+index 09fca7e4dc6..42658c5847e 100644
+--- third_party/waf/waflib/Tools/compiler_cxx.py
++++ third_party/waf/waflib/Tools/compiler_cxx.py
+@@ -37,18 +37,19 @@ from waflib import Utils
+ from waflib.Logs import debug
+
+ cxx_compiler = {
+-'win32': ['msvc', 'g++', 'clang++'],
+-'cygwin': ['g++', 'clang++'],
+-'darwin': ['clang++', 'g++'],
+-'aix': ['xlc++', 'g++', 'clang++'],
+-'linux': ['g++', 'clang++', 'icpc'],
+-'sunos': ['sunc++', 'g++'],
+-'irix': ['g++'],
+-'hpux': ['g++'],
+-'osf1V': ['g++'],
+-'gnu': ['g++', 'clang++'],
+-'java': ['g++', 'msvc', 'clang++', 'icpc'],
+-'default': ['clang++', 'g++']
++'win32': ['msvc', 'g++', 'clang++'],
++'cygwin': ['g++', 'clang++'],
++'darwin': ['clang++', 'g++'],
++'aix': ['xlc++', 'g++', 'clang++'],
++'linux': ['g++', 'clang++', 'icpc'],
++'sunos': ['sunc++', 'g++'],
++'irix': ['g++'],
++'hpux': ['g++'],
++'osf1V': ['g++'],
++'gnu': ['g++', 'clang++'],
++'java': ['g++', 'msvc', 'clang++', 'icpc'],
++'gnukfreebsd': ['g++', 'clang++'],
++'default': ['clang++', 'g++']
+ }
+ """
+ Dict mapping the platform names to Waf tools finding specific C++ compilers::
+diff --git third_party/waf/waflib/Tools/python.py third_party/waf/waflib/Tools/python.py
+index 07442561dff..fb641e5e20d 100644
+--- third_party/waf/waflib/Tools/python.py
++++ third_party/waf/waflib/Tools/python.py
+@@ -416,9 +416,14 @@ def check_python_headers(conf, features='pyembed pyext'):
+
+ if not result:
+ path = [os.path.join(dct['prefix'], "libs")]
+- conf.to_log("\n\n# try again with -L$prefix/libs, and pythonXY name rather than pythonX.Y (win32)\n")
++ conf.to_log("\n\n# try again with -L$prefix/libs, and pythonXY rather than pythonX.Y (win32)\n")
+ result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in $prefix/libs' % name)
+
++ if not result:
++ path = [os.path.normpath(os.path.join(dct['INCLUDEPY'], '..', 'libs'))]
++ conf.to_log("\n\n# try again with -L$INCLUDEPY/../libs, and pythonXY rather than pythonX.Y (win32)\n")
++ result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in $INCLUDEPY/../libs' % name)
++
+ if result:
+ break # do not forget to set LIBPATH_PYEMBED
+
+diff --git third_party/waf/waflib/Tools/qt5.py third_party/waf/waflib/Tools/qt5.py
+index 82c83e18c8a..b3e61325e50 100644
+--- third_party/waf/waflib/Tools/qt5.py
++++ third_party/waf/waflib/Tools/qt5.py
+@@ -783,8 +783,8 @@ def set_qt5_libs_to_check(self):
+ pat = self.env.cxxstlib_PATTERN
+ if Utils.unversioned_sys_platform() == 'darwin':
+ pat = r"%s\.framework"
+- re_qt = re.compile(pat % 'Qt5?(?P<name>\\D+)' + '$')
+- for x in dirlst:
++ re_qt = re.compile(pat % 'Qt5?(?P<name>\\w+)' + '$')
++ for x in sorted(dirlst):
+ m = re_qt.match(x)
+ if m:
+ self.qt5_vars.append("Qt5%s" % m.group('name'))
+diff --git third_party/waf/waflib/Tools/winres.py third_party/waf/waflib/Tools/winres.py
+index 9be1ed66009..73c0e95315b 100644
+--- third_party/waf/waflib/Tools/winres.py
++++ third_party/waf/waflib/Tools/winres.py
+@@ -4,10 +4,12 @@
+
+ "Process *.rc* files for C/C++: X{.rc -> [.res|.rc.o]}"
+
++import os
+ import re
+ from waflib import Task
+ from waflib.TaskGen import extension
+ from waflib.Tools import c_preproc
++from waflib import Utils
+
+ @extension('.rc')
+ def rc_file(self, node):
+@@ -61,6 +63,39 @@ class winrc(Task.Task):
+ tmp.start(self.inputs[0], self.env)
+ return (tmp.nodes, tmp.names)
+
++ def exec_command(self, cmd, **kw):
++ if self.env.WINRC_TGT_F == '/fo':
++ # Since winres include paths may contain spaces, they do not fit in
++ # response files and are best passed as environment variables
++ replace_cmd = []
++ incpaths = []
++ while cmd:
++ # filter include path flags
++ flag = cmd.pop(0)
++ if flag.upper().startswith('/I'):
++ if len(flag) == 2:
++ incpaths.append(cmd.pop(0))
++ else:
++ incpaths.append(flag[2:])
++ else:
++ replace_cmd.append(flag)
++ cmd = replace_cmd
++ if incpaths:
++ # append to existing environment variables in INCLUDE
++ env = kw['env'] = dict(kw.get('env') or self.env.env or os.environ)
++ pre_includes = env.get('INCLUDE', '')
++ env['INCLUDE'] = pre_includes + os.pathsep + os.pathsep.join(incpaths)
++
++ return super(winrc, self).exec_command(cmd, **kw)
++
++ def quote_flag(self, flag):
++ if self.env.WINRC_TGT_F == '/fo':
++ # winres does not support quotes around flags in response files
++ return flag
++
++ return super(winrc, self).quote_flag(flag)
++
++
+ def configure(conf):
+ """
+ Detects the programs RC or windres, depending on the C/C++ compiler in use
+diff --git third_party/waf/waflib/extras/clang_compilation_database.py third_party/waf/waflib/extras/clang_compilation_database.py
+index 17f66949376..bd29db93fd5 100644
+--- third_party/waf/waflib/extras/clang_compilation_database.py
++++ third_party/waf/waflib/extras/clang_compilation_database.py
+@@ -126,7 +126,7 @@ def patch_execute():
+ Invoke clangdb command before build
+ """
+ if self.cmd.startswith('build'):
+- Scripting.run_command('clangdb')
++ Scripting.run_command(self.cmd.replace('build','clangdb'))
+
+ old_execute_build(self)
+
+diff --git third_party/waf/waflib/extras/classic_runner.py third_party/waf/waflib/extras/classic_runner.py
+new file mode 100644
+index 00000000000..b08c794e880
+--- /dev/null
++++ third_party/waf/waflib/extras/classic_runner.py
+@@ -0,0 +1,68 @@
++#!/usr/bin/env python
++# encoding: utf-8
++# Thomas Nagy, 2021 (ita)
++
++from waflib import Utils, Runner
++
++"""
++Re-enable the classic threading system from waf 1.x
++
++def configure(conf):
++ conf.load('classic_runner')
++"""
++
++class TaskConsumer(Utils.threading.Thread):
++ """
++ Task consumers belong to a pool of workers
++
++ They wait for tasks in the queue and then use ``task.process(...)``
++ """
++ def __init__(self, spawner):
++ Utils.threading.Thread.__init__(self)
++ """
++ Obtain :py:class:`waflib.Task.TaskBase` instances from this queue.
++ """
++ self.spawner = spawner
++ self.daemon = True
++ self.start()
++
++ def run(self):
++ """
++ Loop over the tasks to execute
++ """
++ try:
++ self.loop()
++ except Exception:
++ pass
++
++ def loop(self):
++ """
++ Obtain tasks from :py:attr:`waflib.Runner.TaskConsumer.ready` and call
++ :py:meth:`waflib.Task.TaskBase.process`. If the object is a function, execute it.
++ """
++ master = self.spawner.master
++ while 1:
++ if not master.stop:
++ try:
++ tsk = master.ready.get()
++ if tsk:
++ tsk.log_display(tsk.generator.bld)
++ master.process_task(tsk)
++ else:
++ break
++ finally:
++ master.out.put(tsk)
++
++class Spawner(object):
++ """
++ Daemon thread that consumes tasks from :py:class:`waflib.Runner.Parallel` producer and
++ spawns a consuming thread :py:class:`waflib.Runner.Consumer` for each
++ :py:class:`waflib.Task.Task` instance.
++ """
++ def __init__(self, master):
++ self.master = master
++ """:py:class:`waflib.Runner.Parallel` producer instance"""
++
++ self.pool = [TaskConsumer(self) for i in range(master.numjobs)]
++
++Runner.Spawner = Spawner
+diff --git third_party/waf/waflib/extras/color_gcc.py third_party/waf/waflib/extras/color_gcc.py
+index b68c5ebf2df..09729035fec 100644
+--- third_party/waf/waflib/extras/color_gcc.py
++++ third_party/waf/waflib/extras/color_gcc.py
+@@ -19,7 +19,7 @@ class ColorGCCFormatter(Logs.formatter):
+ func = frame.f_code.co_name
+ if func == 'exec_command':
+ cmd = frame.f_locals.get('cmd')
+- if isinstance(cmd, list) and ('gcc' in cmd[0] or 'g++' in cmd[0]):
++ if isinstance(cmd, list) and (len(cmd) > 0) and ('gcc' in cmd[0] or 'g++' in cmd[0]):
+ lines = []
+ for line in rec.msg.splitlines():
+ if 'warning: ' in line:
+diff --git third_party/waf/waflib/extras/eclipse.py third_party/waf/waflib/extras/eclipse.py
+index bb787416e9f..49ca9686b7b 100644
+--- third_party/waf/waflib/extras/eclipse.py
++++ third_party/waf/waflib/extras/eclipse.py
+@@ -10,6 +10,9 @@ Usage:
+ def options(opt):
+ opt.load('eclipse')
+
++To add additional targets beside standard ones (configure, dist, install, check)
++the environment ECLIPSE_EXTRA_TARGETS can be set (ie. to ['test', 'lint', 'docs'])
++
+ $ waf configure eclipse
+ """
+
+@@ -25,6 +28,8 @@ cdt_core = oe_cdt + '.core'
+ cdt_bld = oe_cdt + '.build.core'
+ extbuilder_dir = '.externalToolBuilders'
+ extbuilder_name = 'Waf_Builder.launch'
++settings_dir = '.settings'
++settings_name = 'language.settings.xml'
+
+ class eclipse(Build.BuildContext):
+ cmd = 'eclipse'
+@@ -131,9 +136,11 @@ class eclipse(Build.BuildContext):
+ path = p.path_from(self.srcnode)
+
+ if (path.startswith("/")):
+- cpppath.append(path)
++ if path not in cpppath:
++ cpppath.append(path)
+ else:
+- workspace_includes.append(path)
++ if path not in workspace_includes:
++ workspace_includes.append(path)
+
+ if is_cc and path not in source_dirs:
+ source_dirs.append(path)
+@@ -156,6 +163,61 @@ class eclipse(Build.BuildContext):
+ project = self.impl_create_javaproject(javasrcpath, javalibpath)
+ self.write_conf_to_xml('.classpath', project)
+
++ # Create editor language settings to have correct standards applied in IDE, as per project configuration
++ try:
++ os.mkdir(settings_dir)
++ except OSError:
++ pass # Ignore if dir already exists
++
++ lang_settings = Document()
++ project = lang_settings.createElement('project')
++
++ # Language configurations for C and C++ via cdt
++ if hasc:
++ configuration = self.add(lang_settings, project, 'configuration',
++ {'id' : 'org.eclipse.cdt.core.default.config.1', 'name': 'Default'})
++
++ extension = self.add(lang_settings, configuration, 'extension', {'point': 'org.eclipse.cdt.core.LanguageSettingsProvider'})
++
++ provider = self.add(lang_settings, extension, 'provider',
++ { 'copy-of': 'extension',
++ 'id': 'org.eclipse.cdt.ui.UserLanguageSettingsProvider'})
++
++ provider = self.add(lang_settings, extension, 'provider-reference',
++ { 'id': 'org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider',
++ 'ref': 'shared-provider'})
++
++ provider = self.add(lang_settings, extension, 'provider-reference',
++ { 'id': 'org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider',
++ 'ref': 'shared-provider'})
++
++ # C and C++ are kept as separated providers so appropriate flags are used also in mixed projects
++ if self.env.CC:
++ provider = self.add(lang_settings, extension, 'provider',
++ { 'class': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector',
++ 'console': 'false',
++ 'id': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector.1',
++ 'keep-relative-paths' : 'false',
++ 'name': 'CDT GCC Built-in Compiler Settings',
++ 'parameter': '%s %s ${FLAGS} -E -P -v -dD "${INPUTS}"'%(self.env.CC[0],' '.join(self.env['CFLAGS'])),
++ 'prefer-non-shared': 'true' })
++
++ self.add(lang_settings, provider, 'language-scope', { 'id': 'org.eclipse.cdt.core.gcc'})
++
++ if self.env.CXX:
++ provider = self.add(lang_settings, extension, 'provider',
++ { 'class': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector',
++ 'console': 'false',
++ 'id': 'org.eclipse.cdt.managedbuilder.language.settings.providers.GCCBuiltinSpecsDetector.2',
++ 'keep-relative-paths' : 'false',
++ 'name': 'CDT GCC Built-in Compiler Settings',
++ 'parameter': '%s %s ${FLAGS} -E -P -v -dD "${INPUTS}"'%(self.env.CXX[0],' '.join(self.env['CXXFLAGS'])),
++ 'prefer-non-shared': 'true' })
++ self.add(lang_settings, provider, 'language-scope', { 'id': 'org.eclipse.cdt.core.g++'})
++
++ lang_settings.appendChild(project)
++ self.write_conf_to_xml('%s%s%s'%(settings_dir, os.path.sep, settings_name), lang_settings)
++
+ def impl_create_project(self, executable, appname, hasc, hasjava, haspython, waf_executable):
+ doc = Document()
+ projectDescription = doc.createElement('projectDescription')
+@@ -341,6 +403,8 @@ class eclipse(Build.BuildContext):
+ addTargetWrap('dist', False)
+ addTargetWrap('install', False)
+ addTargetWrap('check', False)
++ for addTgt in self.env.ECLIPSE_EXTRA_TARGETS or []:
++ addTargetWrap(addTgt, False)
+
+ storageModule = self.add(doc, cproject, 'storageModule',
+ {'moduleId': 'cdtBuildSystem',
+@@ -348,6 +412,12 @@ class eclipse(Build.BuildContext):
+
+ self.add(doc, storageModule, 'project', {'id': '%s.null.1'%appname, 'name': appname})
+
++ storageModule = self.add(doc, cproject, 'storageModule',
++ {'moduleId': 'org.eclipse.cdt.core.LanguageSettingsProviders'})
++
++ storageModule = self.add(doc, cproject, 'storageModule',
++ {'moduleId': 'scannerConfiguration'})
++
+ doc.appendChild(cproject)
+ return doc
+
+diff --git third_party/waf/waflib/extras/gccdeps.py third_party/waf/waflib/extras/gccdeps.py
+index 1fc9373489a..9e9952f2f7d 100644
+--- third_party/waf/waflib/extras/gccdeps.py
++++ third_party/waf/waflib/extras/gccdeps.py
+@@ -29,13 +29,6 @@ if not c_preproc.go_absolute:
+ # Third-party tools are allowed to add extra names in here with append()
+ supported_compilers = ['gas', 'gcc', 'icc', 'clang']
+
+-def scan(self):
+- if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
+- return super(self.derived_gccdeps, self).scan()
+- nodes = self.generator.bld.node_deps.get(self.uid(), [])
+- names = []
+- return (nodes, names)
+-
+ re_o = re.compile(r"\.o$")
+ re_splitter = re.compile(r'(?<!\\)\s+') # split by space, except when spaces are escaped
+
+@@ -61,28 +54,30 @@ def path_to_node(base_node, path, cached_nodes):
+ else:
+ # Not hashable, assume it is a list and join into a string
+ node_lookup_key = (base_node, os.path.sep.join(path))
++
+ try:
+- lock.acquire()
+ node = cached_nodes[node_lookup_key]
+ except KeyError:
+- node = base_node.find_resource(path)
+- cached_nodes[node_lookup_key] = node
+- finally:
+- lock.release()
++ # retry with lock on cache miss
++ with lock:
++ try:
++ node = cached_nodes[node_lookup_key]
++ except KeyError:
++ node = cached_nodes[node_lookup_key] = base_node.find_resource(path)
++
+ return node
+
+ def post_run(self):
+ if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
+ return super(self.derived_gccdeps, self).post_run()
+
+- name = self.outputs[0].abspath()
+- name = re_o.sub('.d', name)
++ deps_filename = self.outputs[0].abspath()
++ deps_filename = re_o.sub('.d', deps_filename)
+ try:
+- txt = Utils.readf(name)
++ deps_txt = Utils.readf(deps_filename)
+ except EnvironmentError:
+ Logs.error('Could not find a .d dependency file, are cflags/cxxflags overwritten?')
+ raise
+- #os.remove(name)
+
+ # Compilers have the choice to either output the file's dependencies
+ # as one large Makefile rule:
+@@ -102,15 +97,16 @@ def post_run(self):
+ # So the first step is to sanitize the input by stripping out the left-
+ # hand side of all these lines. After that, whatever remains are the
+ # implicit dependencies of task.outputs[0]
+- txt = '\n'.join([remove_makefile_rule_lhs(line) for line in txt.splitlines()])
++ deps_txt = '\n'.join([remove_makefile_rule_lhs(line) for line in deps_txt.splitlines()])
+
+ # Now join all the lines together
+- txt = txt.replace('\\\n', '')
++ deps_txt = deps_txt.replace('\\\n', '')
+
+- val = txt.strip()
+- val = [x.replace('\\ ', ' ') for x in re_splitter.split(val) if x]
++ dep_paths = deps_txt.strip()
++ dep_paths = [x.replace('\\ ', ' ') for x in re_splitter.split(dep_paths) if x]
+
+- nodes = []
++ resolved_nodes = []
++ unresolved_names = []
+ bld = self.generator.bld
+
+ # Dynamically bind to the cache
+@@ -119,39 +115,41 @@ def post_run(self):
+ except AttributeError:
+ cached_nodes = bld.cached_nodes = {}
+
+- for x in val:
++ for path in dep_paths:
+
+ node = None
+- if os.path.isabs(x):
+- node = path_to_node(bld.root, x, cached_nodes)
++ if os.path.isabs(path):
++ node = path_to_node(bld.root, path, cached_nodes)
+ else:
+ # TODO waf 1.9 - single cwd value
+- path = getattr(bld, 'cwdx', bld.bldnode)
++ base_node = getattr(bld, 'cwdx', bld.bldnode)
+ # when calling find_resource, make sure the path does not contain '..'
+- x = [k for k in Utils.split_path(x) if k and k != '.']
+- while '..' in x:
+- idx = x.index('..')
++ path = [k for k in Utils.split_path(path) if k and k != '.']
++ while '..' in path:
++ idx = path.index('..')
+ if idx == 0:
+- x = x[1:]
+- path = path.parent
++ path = path[1:]
++ base_node = base_node.parent
+ else:
+- del x[idx]
+- del x[idx-1]
++ del path[idx]
++ del path[idx-1]
+
+- node = path_to_node(path, x, cached_nodes)
++ node = path_to_node(base_node, path, cached_nodes)
+
+ if not node:
+- raise ValueError('could not find %r for %r' % (x, self))
++ raise ValueError('could not find %r for %r' % (path, self))
++
+ if id(node) == id(self.inputs[0]):
+ # ignore the source file, it is already in the dependencies
+ # this way, successful config tests may be retrieved from the cache
+ continue
+- nodes.append(node)
+
+- Logs.debug('deps: gccdeps for %s returned %s', self, nodes)
++ resolved_nodes.append(node)
+
+- bld.node_deps[self.uid()] = nodes
+- bld.raw_deps[self.uid()] = []
++ Logs.debug('deps: gccdeps for %s returned %s', self, resolved_nodes)
++
++ bld.node_deps[self.uid()] = resolved_nodes
++ bld.raw_deps[self.uid()] = unresolved_names
+
+ try:
+ del self.cache_sig
+@@ -160,6 +158,14 @@ def post_run(self):
+
+ Task.Task.post_run(self)
+
++def scan(self):
++ if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
++ return super(self.derived_gccdeps, self).scan()
++
++ resolved_nodes = self.generator.bld.node_deps.get(self.uid(), [])
++ unresolved_names = []
++ return (resolved_nodes, unresolved_names)
++
+ def sig_implicit_deps(self):
+ if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
+ return super(self.derived_gccdeps, self).sig_implicit_deps()
+diff --git third_party/waf/waflib/extras/msvcdeps.py third_party/waf/waflib/extras/msvcdeps.py
+index 52985dce058..e8985bde7c7 100644
+--- third_party/waf/waflib/extras/msvcdeps.py
++++ third_party/waf/waflib/extras/msvcdeps.py
+@@ -32,7 +32,6 @@ from waflib.Tools import c_preproc, c, cxx, msvc
+ from waflib.TaskGen import feature, before_method
+
+ lock = threading.Lock()
+-nodes = {} # Cache the path -> Node lookup
+
+ PREPROCESSOR_FLAG = '/showIncludes'
+ INCLUDE_PATTERN = 'Note: including file:'
+@@ -50,23 +49,47 @@ def apply_msvcdeps_flags(taskgen):
+ if taskgen.env.get_flat(flag).find(PREPROCESSOR_FLAG) < 0:
+ taskgen.env.append_value(flag, PREPROCESSOR_FLAG)
+
++
++def get_correct_path_case(base_path, path):
++ '''
++ Return a case-corrected version of ``path`` by searching the filesystem for
++ ``path``, relative to ``base_path``, using the case returned by the filesystem.
++ '''
++ components = Utils.split_path(path)
++
++ corrected_path = ''
++ if os.path.isabs(path):
++ corrected_path = components.pop(0).upper() + os.sep
++
++ for part in components:
++ part = part.lower()
++ search_path = os.path.join(base_path, corrected_path)
++ if part == '..':
++ corrected_path = os.path.join(corrected_path, part)
++ search_path = os.path.normpath(search_path)
++ continue
++
++ for item in sorted(os.listdir(search_path)):
++ if item.lower() == part:
++ corrected_path = os.path.join(corrected_path, item)
++ break
++ else:
++ raise ValueError("Can't find %r in %r" % (part, search_path))
++
++ return corrected_path
++
++
+ def path_to_node(base_node, path, cached_nodes):
+ '''
+ Take the base node and the path and return a node
+ Results are cached because searching the node tree is expensive
+ The following code is executed by threads, it is not safe, so a lock is needed...
+ '''
+- # normalize the path because ant_glob() does not understand
+- # parent path components (..)
++ # normalize the path to remove parent path components (..)
+ path = os.path.normpath(path)
+
+ # normalize the path case to increase likelihood of a cache hit
+- path = os.path.normcase(path)
+-
+- # ant_glob interprets [] and () characters, so those must be replaced
+- path = path.replace('[', '?').replace(']', '?').replace('(', '[(]').replace(')', '[)]')
+-
+- node_lookup_key = (base_node, path)
++ node_lookup_key = (base_node, os.path.normcase(path))
+
+ try:
+ node = cached_nodes[node_lookup_key]
+@@ -76,8 +99,8 @@ def path_to_node(base_node, path, cached_nodes):
+ try:
+ node = cached_nodes[node_lookup_key]
+ except KeyError:
+- node_list = base_node.ant_glob([path], ignorecase=True, remove=False, quiet=True, regex=False)
+- node = cached_nodes[node_lookup_key] = node_list[0] if node_list else None
++ path = get_correct_path_case(base_node.abspath(), path)
++ node = cached_nodes[node_lookup_key] = base_node.find_node(path)
+
+ return node
+
+@@ -89,9 +112,9 @@ def post_run(self):
+ if getattr(self, 'cached', None):
+ return Task.Task.post_run(self)
+
+- bld = self.generator.bld
+- unresolved_names = []
+ resolved_nodes = []
++ unresolved_names = []
++ bld = self.generator.bld
+
+ # Dynamically bind to the cache
+ try:
+@@ -124,11 +147,14 @@ def post_run(self):
+ continue
+
+ if id(node) == id(self.inputs[0]):
+- # Self-dependency
++ # ignore the source file, it is already in the dependencies
++ # this way, successful config tests may be retrieved from the cache
+ continue
+
+ resolved_nodes.append(node)
+
++ Logs.debug('deps: msvcdeps for %s returned %s', self, resolved_nodes)
++
+ bld.node_deps[self.uid()] = resolved_nodes
+ bld.raw_deps[self.uid()] = unresolved_names
+
+diff --git third_party/waf/waflib/extras/msvs.py third_party/waf/waflib/extras/msvs.py
+index 8aa2db0b751..03b739f849c 100644
+--- third_party/waf/waflib/extras/msvs.py
++++ third_party/waf/waflib/extras/msvs.py
+@@ -787,8 +787,12 @@ class msvs_generator(BuildContext):
+ self.collect_dirs()
+ default_project = getattr(self, 'default_project', None)
+ def sortfun(x):
+- if x.name == default_project:
++ # folders should sort to the top
++ if getattr(x, 'VS_GUID_SOLUTIONFOLDER', None):
+ return ''
++ # followed by the default project
++ elif x.name == default_project:
++ return ' '
+ return getattr(x, 'path', None) and x.path.win32path() or x.name
+ self.all_projects.sort(key=sortfun)
+
+diff --git third_party/waf/waflib/extras/swig.py third_party/waf/waflib/extras/swig.py
+index 740ab46d963..967caeb5a82 100644
+--- third_party/waf/waflib/extras/swig.py
++++ third_party/waf/waflib/extras/swig.py
+@@ -17,7 +17,7 @@ tasks have to be added dynamically:
+
+ SWIG_EXTS = ['.swig', '.i']
+
+-re_module = re.compile(r'%module(?:\s*\(.*\))?\s+(.+)', re.M)
++re_module = re.compile(r'%module(?:\s*\(.*\))?\s+([^\r\n]+)', re.M)
+
+ re_1 = re.compile(r'^%module.*?\s+([\w]+)\s*?$', re.M)
+ re_2 = re.compile(r'[#%](?:include|import(?:\(module=".*"\))+|python(?:begin|code)) [<"](.*)[">]', re.M)
+diff --git third_party/waf/waflib/extras/wafcache.py third_party/waf/waflib/extras/wafcache.py
+index cc23fcd6673..2cef46c0e1c 100644
+--- third_party/waf/waflib/extras/wafcache.py
++++ third_party/waf/waflib/extras/wafcache.py
+@@ -258,6 +258,19 @@ def build(bld):
+ """
+ Called during the build process to enable file caching
+ """
++ if WAFCACHE_STATS:
++ # Init counter for statistics and hook to print results at the end
++ bld.cache_reqs = bld.cache_hits = bld.cache_puts = 0
++
++ def printstats(bld):
++ hit_ratio = 0
++ if bld.cache_reqs > 0:
++ hit_ratio = (bld.cache_hits / bld.cache_reqs) * 100
++ Logs.pprint('CYAN', ' wafcache stats: requests: %s, hits, %s, ratio: %.2f%%, writes %s' %
++ (bld.cache_reqs, bld.cache_hits, hit_ratio, bld.cache_puts) )
++
++ bld.add_post_fun(printstats)
++
+ if process_pool:
+ # already called once
+ return
+@@ -273,19 +286,6 @@ def build(bld):
+ for x in reversed(list(Task.classes.values())):
+ make_cached(x)
+
+- if WAFCACHE_STATS:
+- # Init counter for statistics and hook to print results at the end
+- bld.cache_reqs = bld.cache_hits = bld.cache_puts = 0
+-
+- def printstats(bld):
+- hit_ratio = 0
+- if bld.cache_reqs > 0:
+- hit_ratio = (bld.cache_hits / bld.cache_reqs) * 100
+- Logs.pprint('CYAN', ' wafcache stats: requests: %s, hits, %s, ratio: %.2f%%, writes %s' %
+- (bld.cache_reqs, bld.cache_hits, hit_ratio, bld.cache_puts) )
+-
+- bld.add_post_fun(printstats)
+-
+ def cache_command(sig, files_from, files_to):
+ """
+ Create a command for cache worker processes, returns a pickled
+diff --git third_party/waf/waflib/fixpy2.py third_party/waf/waflib/fixpy2.py
+index 24176e06645..c99bff4b9ae 100644
+--- third_party/waf/waflib/fixpy2.py
++++ third_party/waf/waflib/fixpy2.py
+@@ -56,7 +56,7 @@ def r1(code):
+ @subst('Runner.py')
+ def r4(code):
+ "generator syntax"
+- return code.replace('next(self.biter)', 'self.biter.next()')
++ return code.replace('next(self.biter)', 'self.biter.next()').replace('self.daemon = True', 'self.setDaemon(1)')
+
+ @subst('Context.py')
+ def r5(code):
+--
+2.37.3
+
diff --git a/net/samba413/files/patch-waf-2.0.24 b/net/samba413/files/patch-waf-2.0.24
new file mode 100644
index 000000000000..2c5c76e6ca3b
--- /dev/null
+++ b/net/samba413/files/patch-waf-2.0.24
@@ -0,0 +1,164 @@
+From d19dfe1efb2f6cb0dcf0a63b957df584d8ed5945 Mon Sep 17 00:00:00 2001
+From: Andreas Schneider <asn@samba.org>
+Date: Mon, 23 May 2022 07:54:06 +0200
+Subject: [PATCH] third_party: Update waf to version 2.0.24
+
+This fixes building of python libraries with Python 3.11!
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15071
+
+Signed-off-by: Andreas Schneider <asn@samba.org>
+Reviewed-by: Stefan Metzmacher <metze@samba.org>
+
+Autobuild-User(master): Andreas Schneider <asn@cryptomilk.org>
+Autobuild-Date(master): Mon May 23 09:34:51 UTC 2022 on sn-devel-184
+---
+ buildtools/bin/waf | 2 +-
+ buildtools/wafsamba/wafsamba.py | 2 +-
+ third_party/waf/waflib/Context.py | 8 ++++----
+ third_party/waf/waflib/Tools/ccroot.py | 1 +
+ third_party/waf/waflib/Tools/msvc.py | 17 ++++++++++++++++-
+ third_party/waf/waflib/Tools/python.py | 4 ++--
+ third_party/waf/waflib/Tools/tex.py | 1 +
+ 7 files changed, 26 insertions(+), 9 deletions(-)
+
+diff --git buildtools/bin/waf buildtools/bin/waf
+index 2001ccdbd8a..d9cba343623 100755
+--- buildtools/bin/waf
++++ buildtools/bin/waf
+@@ -32,7 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
+
+ import os, sys, inspect
+
+-VERSION="2.0.23"
++VERSION="2.0.24"
+ REVISION="x"
+ GIT="x"
+ INSTALL="x"
+diff --git buildtools/wafsamba/wafsamba.py buildtools/wafsamba/wafsamba.py
+index 4bd4e9f7fe3..79fe8b5e575 100644
+--- buildtools/wafsamba/wafsamba.py
++++ buildtools/wafsamba/wafsamba.py
+@@ -38,7 +38,7 @@ LIB_PATH="shared"
+
+ os.environ['PYTHONUNBUFFERED'] = '1'
+
+-if Context.HEXVERSION not in (0x2001700,):
++if Context.HEXVERSION not in (0x2001800,):
+ Logs.error('''
+ Please use the version of waf that comes with Samba, not
+ a system installed version. See http://wiki.samba.org/index.php/Waf
+diff --git third_party/waf/waflib/Context.py third_party/waf/waflib/Context.py
+index 36d1ca74fef..4a0130b24a0 100644
+--- third_party/waf/waflib/Context.py
++++ third_party/waf/waflib/Context.py
+@@ -18,13 +18,13 @@ else:
+ import imp
+
+ # the following 3 constants are updated on each new release (do not touch)
+-HEXVERSION=0x2001700
++HEXVERSION=0x2001800
+ """Constant updated on new releases"""
+
+-WAFVERSION="2.0.23"
++WAFVERSION="2.0.24"
+ """Constant updated on new releases"""
+
+-WAFREVISION="cc6b34cf555d354c34f554c41206134072588de7"
++WAFREVISION="1af97c71f5a6756abf36d0f78ed8fd551596d7cb"
+ """Git revision when the waf version is updated"""
+
+ WAFNAME="waf"
+@@ -144,7 +144,7 @@ class Context(ctx):
+ :type fun: string
+
+ .. inheritance-diagram:: waflib.Context.Context waflib.Build.BuildContext waflib.Build.InstallContext waflib.Build.UninstallContext waflib.Build.StepContext waflib.Build.ListContext waflib.Configure.ConfigurationContext waflib.Scripting.Dist waflib.Scripting.DistCheck waflib.Build.CleanContext
+-
++ :top-classes: waflib.Context.Context
+ """
+
+ errors = Errors
+diff --git third_party/waf/waflib/Tools/ccroot.py third_party/waf/waflib/Tools/ccroot.py
+index 579d5b2b72b..76deff54dcb 100644
+--- third_party/waf/waflib/Tools/ccroot.py
++++ third_party/waf/waflib/Tools/ccroot.py
+@@ -128,6 +128,7 @@ class link_task(Task.Task):
+ Base class for all link tasks. A task generator is supposed to have at most one link task bound in the attribute *link_task*. See :py:func:`waflib.Tools.ccroot.apply_link`.
+
+ .. inheritance-diagram:: waflib.Tools.ccroot.stlink_task waflib.Tools.c.cprogram waflib.Tools.c.cshlib waflib.Tools.cxx.cxxstlib waflib.Tools.cxx.cxxprogram waflib.Tools.cxx.cxxshlib waflib.Tools.d.dprogram waflib.Tools.d.dshlib waflib.Tools.d.dstlib waflib.Tools.ccroot.fake_shlib waflib.Tools.ccroot.fake_stlib waflib.Tools.asm.asmprogram waflib.Tools.asm.asmshlib waflib.Tools.asm.asmstlib
++ :top-classes: waflib.Tools.ccroot.link_task
+ """
+ color = 'YELLOW'
+
+diff --git third_party/waf/waflib/Tools/msvc.py third_party/waf/waflib/Tools/msvc.py
+index 0c4703aaee9..026a4c7fc48 100644
+--- third_party/waf/waflib/Tools/msvc.py
++++ third_party/waf/waflib/Tools/msvc.py
+@@ -109,6 +109,21 @@ def options(opt):
+ opt.add_option('--msvc_targets', type='string', help = 'msvc targets, eg: "x64,arm"', default='')
+ opt.add_option('--no-msvc-lazy', action='store_false', help = 'lazily check msvc target environments', default=True, dest='msvc_lazy')
+
++class MSVCVersion(object):
++ def __init__(self, ver):
++ m = re.search('^(.*)\s+(\d+[.]\d+)', ver)
++ if m:
++ self.name = m.group(1)
++ self.number = float(m.group(2))
++ else:
++ self.name = ver
++ self.number = 0.
++
++ def __lt__(self, other):
++ if self.number == other.number:
++ return self.name < other.name
++ return self.number < other.number
++
+ @conf
+ def setup_msvc(conf, versiondict):
+ """
+@@ -125,7 +140,7 @@ def setup_msvc(conf, versiondict):
+ platforms=Utils.to_list(conf.env.MSVC_TARGETS) or [i for i,j in all_msvc_platforms+all_icl_platforms+all_wince_platforms]
+ desired_versions = getattr(Options.options, 'msvc_version', '').split(',')
+ if desired_versions == ['']:
+- desired_versions = conf.env.MSVC_VERSIONS or list(reversed(sorted(versiondict.keys())))
++ desired_versions = conf.env.MSVC_VERSIONS or list(sorted(versiondict.keys(), key=MSVCVersion, reverse=True))
+
+ # Override lazy detection by evaluating after the fact.
+ lazy_detect = getattr(Options.options, 'msvc_lazy', True)
+diff --git third_party/waf/waflib/Tools/python.py third_party/waf/waflib/Tools/python.py
+index fb641e5e20d..a23bd019335 100644
+--- third_party/waf/waflib/Tools/python.py
++++ third_party/waf/waflib/Tools/python.py
+@@ -315,7 +315,7 @@ def check_python_headers(conf, features='pyembed pyext'):
+ conf.fatal('Could not find the python executable')
+
+ # so we actually do all this for compatibility reasons and for obtaining pyext_PATTERN below
+- v = 'prefix SO LDFLAGS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET LDSHARED CFLAGS LDVERSION'.split()
++ v = 'prefix SO EXT_SUFFIX LDFLAGS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET LDSHARED CFLAGS LDVERSION'.split()
+ try:
+ lst = conf.get_python_variables(["get_config_var('%s') or ''" % x for x in v])
+ except RuntimeError:
+@@ -328,7 +328,7 @@ def check_python_headers(conf, features='pyembed pyext'):
+ x = 'MACOSX_DEPLOYMENT_TARGET'
+ if dct[x]:
+ env[x] = conf.environ[x] = str(dct[x])
+- env.pyext_PATTERN = '%s' + dct['SO'] # not a mistake
++ env.pyext_PATTERN = '%s' + (dct['EXT_SUFFIX'] or dct['SO']) # SO is deprecated in 3.5 and removed in 3.11
+
+
+ # Try to get pythonX.Y-config
+diff --git third_party/waf/waflib/Tools/tex.py third_party/waf/waflib/Tools/tex.py
+index eaf9fdb5802..b4792c3fe87 100644
+--- third_party/waf/waflib/Tools/tex.py
++++ third_party/waf/waflib/Tools/tex.py
+@@ -90,6 +90,7 @@ class tex(Task.Task):
+ Compiles a tex/latex file.
+
+ .. inheritance-diagram:: waflib.Tools.tex.latex waflib.Tools.tex.xelatex waflib.Tools.tex.pdflatex
++ :top-classes: waflib.Tools.tex.tex
+ """
+
+ bibtex_fun, _ = Task.compile_fun('${BIBTEX} ${BIBTEXFLAGS} ${SRCFILE}', shell=False)
+--
+2.37.3
+
diff --git a/net/samba413/pkg-plist.ad_dc b/net/samba413/pkg-plist.ad_dc
index 13dbcc536782..b7c8a91fbba4 100644
--- a/net/samba413/pkg-plist.ad_dc
+++ b/net/samba413/pkg-plist.ad_dc
@@ -5,7 +5,6 @@ sbin/samba_dnsupdate
sbin/samba_kcc
sbin/samba_spnupdate
sbin/samba_upgradedns
-sbin/samba-gpupdate
include/samba4/dcerpc_server.h
lib/samba4/libdcerpc-server.so
lib/samba4/libdcerpc-server.so.0
@@ -172,4 +171,3 @@ lib/samba4/private/libscavenge-dns-records-samba4.so
@dir %%DATADIR%%/setup/ad-schema
@dir %%DATADIR%%/setup
@dir %%DATADIR%%
-man/man8/samba-gpupdate.8.gz
diff --git a/net/samba413/pkg-plist.python b/net/samba413/pkg-plist.python
index c931ab22f63e..6de93c2891d0 100644
--- a/net/samba413/pkg-plist.python
+++ b/net/samba413/pkg-plist.python
@@ -1,5 +1,7 @@
bin/smbtorture
+sbin/samba-gpupdate
man/man1/smbtorture.1.gz
+man/man8/samba-gpupdate.8.gz
include/samba4/policy.h
lib/samba4/libsamba-policy%%PYTHON_EXT_SUFFIX%%.so
lib/samba4/libsamba-policy%%PYTHON_EXT_SUFFIX%%.so.0
@@ -258,6 +260,7 @@ lib/samba4/private/libsamba-python%%PYTHON_EXT_SUFFIX%%-samba4.so
%%PYTHON_SITELIBDIR%%/samba/tests/krb5/kdc_base_test.py
%%PYTHON_SITELIBDIR%%/samba/tests/krb5/kdc_tests.py
%%PYTHON_SITELIBDIR%%/samba/tests/krb5/kdc_tgs_tests.py
+%%PYTHON_SITELIBDIR%%/samba/tests/krb5/kpasswd_tests.py
%%PYTHON_SITELIBDIR%%/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py
%%PYTHON_SITELIBDIR%%/samba/tests/krb5/raw_testcase.py
%%PYTHON_SITELIBDIR%%/samba/tests/krb5/rfc4120_constants.py