[Pkg-samba-maint] samba and ldb updates for bullseye-security

Michael Tokarev mjt at tls.msk.ru
Mon Aug 1 17:19:31 BST 2022


Below is the proposed updated packages for bullseye-security for
samba and ldb. Since the two are closely related I'm including both
in the same request.

The vulnerabilities fixed are known and has been disclosed at Jul-27,
but unfortunately I failed to process them before that date.

Here are the changelogs.

  ldb (2:2.2.3-2~deb11u2) bullseye-security; urgency=medium
  .
    * d/control: add myself to Uploaders
    * ldb-memory-bug-15096-CVE-2022-32745-4.13-v3.patch:
      only the lib/ldb/* bits from the larger upstream patchset as
      found at https://bugzilla.samba.org/show_bug.cgi?id=15096 , as
      part of the fix for CVE-2022-32745
    * d/*.symbols*: add new symbols and versions

this is a preparational change which is required by the change in
samba which follows.

This is a single patchset prepared and verified by the upstream.
Since in Debian bullseye we used to use two separate source packages
(samba and ldb; in current testing both are built from the same
source), I had to strip non-ldb-related changes from this patchset
to be able to apply it to ldb source.  Some upstream changes touched
both samba and ldb in the same patch.

The patch adds a bunch of new symbols, which are added to d/symbols
files.  And since this is my first upload of ldb, I'm adding myself
to the list of Uploaders.

And here are the samba changes:

  samba (2:4.13.13+dfsg-1~deb11u5) stable-security; urgency=medium
  .
    * 3 patches:
      - CVE-2022-32742-bug-15085-4.13.patch
      - kpasswd_bugs_v15_4-13.patch
      - ldb-memory-bug-15096-4.13-v3.patch
      fixing:
      o CVE-2022-2031: Samba AD users can bypass certain restrictions associated
        with changing passwords.
        https://www.samba.org/samba/security/CVE-2022-2031.html
      o CVE-2022-32742: Server memory information leak via SMB1.
        https://www.samba.org/samba/security/CVE-2022-32742.html
      o CVE-2022-32744: Samba AD users can forge password change requests
        for any user.
        https://www.samba.org/samba/security/CVE-2022-32744.html
      o CVE-2022-32745: Samba AD users can crash the server process with an LDAP
        add or modify request.
        https://www.samba.org/samba/security/CVE-2022-32745.html
      o CVE-2022-32746: Samba AD users can induce a use-after-free in the server
        process with an LDAP add or modify request.
        https://www.samba.org/samba/security/CVE-2022-32746.html
     * Closes: #1016449, CVE-2022-2031 CVE-2022-32742, CVE-2022-32744,
       CVE-2022-32745, CVE-2022-32746
     * Build-Depend on libldb-dev >= 2.2.3-2~deb11u2
       (which includes the new symbols in libldb used by this update)

These are actual patches taken from upstream. With small change: I removed
additions and deletions of files in tests/knownfail/ which were in the same
patchset, since quilt does not handle this situation.

Patchset ldb-memory-bug-15096-4.13-v3.patch is the one which is partially
included by ldb-memory-bug-15096-CVE-2022-32745-4.13-v3.patch in ldb package.
This time it is a complete upstream changeset (minus the knownfail/ removals
mentioned above) - it includes ldb changes too, which are applied anyway but
aren't actually used in debian - just to make the patchset to be the same as
has been prepared and tested by upstream.

The diffstats:

ldb_2.2.3-2~deb11u2.debdiff
  changelog                                                 |   11
  control                                                   |    3
  libldb2.symbols                                           |    9
  patches/ldb-memory-bug-15096-CVE-2022-32745-4.13-v3.patch | 1012 ++++++++++++++
  patches/series                                            |    1
  python3-ldb.symbols.in                                    |    3
  6 files changed, 1037 insertions(+), 2 deletions(-)

samba_4.13.13+dfsg-1~deb11u5.debdiff
  changelog                                   |   28
  control                                     |    2
  patches/CVE-2022-32742-bug-15085-4.13.patch |  198
  patches/kpasswd_bugs_v15_4-13.patch         |11325 ++++++++++++++++++++++++++++
  patches/ldb-memory-bug-15096-4.13-v3.patch  | 2341 +++++
  patches/series                              |    3
  6 files changed, 13896 insertions(+), 1 deletion(-)

The majority of the change is kpasswd_bugs_v15_4-13.patch which comes from heimdal
source.

The samba itself, with these changes, has been tested by the upstream and
passed the upstream testsuite.  I myself does not have a test environment where
I can test the issues being fixed. Basic functionality, including some minimal
AD work, seems to be okay though.

However, there's one possible breakage: libldb2 is used not only by samba but
alsp by sssd package. So far I weren't able to test if sssd binaries continue
to work after updating libldb2.  Timo Aaltonen (tjaalton at kapsi.fi) - who is the
sssd maintainer - were not able to test my libldb build with sssd and I don't
know neither knowlege about sssd nor the test environment for it.  Maybe I should
check if other distributions already faced any issues there...

The whole thing seems to be okay, but overall, samba in bullseye has so many
issues it's difficult to have proper fixing there. Also I had only limited amount
of time to work with the update, but the issues seems to be serious enough to
have much quicker update. Oh well.

The debdiffs against the current versions in debian are attached.

I'll try to do some more testing but the whole thing looks more or less sane
now.

Thanks,

/mjt
-------------- next part --------------
diff -Nru ldb-2.2.3/debian/changelog ldb-2.2.3/debian/changelog
--- ldb-2.2.3/debian/changelog	2021-11-04 22:08:35.000000000 +0300
+++ ldb-2.2.3/debian/changelog	2022-08-01 17:18:51.000000000 +0300
@@ -1,3 +1,14 @@
+ldb (2:2.2.3-2~deb11u2) bullseye-security; urgency=medium
+
+  * d/control: add myself to Uploaders
+  * ldb-memory-bug-15096-CVE-2022-32745-4.13-v3.patch:
+    only the lib/ldb/* bits from the larger upstream patchset as
+    found at https://bugzilla.samba.org/show_bug.cgi?id=15096 , as
+    part of the fix for CVE-2022-32745
+  * d/*.symbols*: add new symbols and versions
+
+ -- Michael Tokarev <mjt at tls.msk.ru>  Mon, 01 Aug 2022 17:18:51 +0300
+
 ldb (2:2.2.3-2~deb11u1) bullseye-security; urgency=high
 
   * Upload to bullseye-security
diff -Nru ldb-2.2.3/debian/control ldb-2.2.3/debian/control
--- ldb-2.2.3/debian/control	2021-10-30 16:53:23.000000000 +0300
+++ ldb-2.2.3/debian/control	2022-08-01 17:18:51.000000000 +0300
@@ -3,7 +3,8 @@
 Priority: optional
 Maintainer: Debian Samba Maintainers <pkg-samba-maint at lists.alioth.debian.org>
 Uploaders: Jelmer Vernooij <jelmer at debian.org>,
-           Mathieu Parent <sathieu at debian.org>
+           Mathieu Parent <sathieu at debian.org>,
+           Michael Tokarev <mjt at tls.msk.ru>
 Build-Depends: dh-exec,
                dh-python,
                docbook-xml,
diff -Nru ldb-2.2.3/debian/libldb2.symbols ldb-2.2.3/debian/libldb2.symbols
--- ldb-2.2.3/debian/libldb2.symbols	2021-11-04 22:03:57.000000000 +0300
+++ ldb-2.2.3/debian/libldb2.symbols	2022-08-01 17:18:51.000000000 +0300
@@ -75,6 +75,7 @@
  LDB_2.2.1 at LDB_2.2.1 2:2.2.1
  LDB_2.2.2 at LDB_2.2.2 2:2.2.2
  LDB_2.2.3 at LDB_2.2.3 2:2.2.3
+ LDB_2.2.4 at LDB_2.2.4 2:2.2.3-2~deb11u2~
  ldb_check_critical_controls at LDB_0.9.22 0.9.22
  ldb_controls_except_specified at LDB_0.9.22 0.9.22
  ldb_control_to_string at LDB_1.0.2 1.0.2~git20110403
@@ -233,7 +234,14 @@
  ldb_msg_add_steal_string at LDB_0.9.10 0.9.21
  ldb_msg_add_steal_value at LDB_0.9.10 0.9.21
  ldb_msg_add_string at LDB_0.9.10 0.9.21
+ ldb_msg_add_string_flags at LDB_2.2.4 2:2.2.3-2~deb11u2~
  ldb_msg_add_value at LDB_0.9.10 0.9.21
+ ldb_msg_append_fmt at LDB_2.2.4 2:2.2.3-2~deb11u2~
+ ldb_msg_append_linearized_dn at LDB_2.2.4 2:2.2.3-2~deb11u2~
+ ldb_msg_append_steal_string at LDB_2.2.4 2:2.2.3-2~deb11u2~
+ ldb_msg_append_steal_value at LDB_2.2.4 2:2.2.3-2~deb11u2~
+ ldb_msg_append_string at LDB_2.2.4 2:2.2.3-2~deb11u2~
+ ldb_msg_append_value at LDB_2.2.4 2:2.2.3-2~deb11u2~
  ldb_msg_canonicalize at LDB_0.9.10 0.9.21
  ldb_msg_check_string_attribute at LDB_0.9.10 0.9.21
  ldb_msg_copy at LDB_0.9.10 0.9.21
@@ -241,6 +249,7 @@
  ldb_msg_copy_shallow at LDB_0.9.10 0.9.21
  ldb_msg_diff at LDB_0.9.10 0.9.21
  ldb_msg_difference at LDB_0.9.15 0.9.21
+ ldb_msg_element_add_value at LDB_2.2.4 2:2.2.3-2~deb11u2~
  ldb_msg_element_compare at LDB_0.9.10 0.9.21
  ldb_msg_element_compare_name at LDB_0.9.10 0.9.21
  ldb_msg_element_equal_ordered at LDB_1.1.6 1:1.1.6
diff -Nru ldb-2.2.3/debian/patches/ldb-memory-bug-15096-CVE-2022-32745-4.13-v3.patch ldb-2.2.3/debian/patches/ldb-memory-bug-15096-CVE-2022-32745-4.13-v3.patch
--- ldb-2.2.3/debian/patches/ldb-memory-bug-15096-CVE-2022-32745-4.13-v3.patch	1970-01-01 03:00:00.000000000 +0300
+++ ldb-2.2.3/debian/patches/ldb-memory-bug-15096-CVE-2022-32745-4.13-v3.patch	2022-08-01 17:18:51.000000000 +0300
@@ -0,0 +1,1012 @@
+From 07b8a1d0899f016bb313eb1b961c0fe04330ab19 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 16 Feb 2022 12:43:52 +1300
+Subject: [PATCH 06/18] 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 at catalyst.net.nz>
+---
+ modules/rdn_name.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/modules/rdn_name.c b/modules/rdn_name.c
+index e69ad9315ae..25cffe07591 100644
+--- a/modules/rdn_name.c
++++ b/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.35.0
+
+
+From 37a606628f14d585ea12e1dd4e680dda92ce4620 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Mon, 21 Feb 2022 16:10:32 +1300
+Subject: [PATCH 10/18] 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 at catalyst.net.nz>
+---
+ common/ldb_msg.c     | 43 +++++++++++++++++++++++++++++++-----
+ include/ldb_module.h |  6 +++++
+ 2 files changed, 43 insertions(+), 6 deletions(-)
+
+diff --git a/common/ldb_msg.c b/common/ldb_msg.c
+index 57dfc5a04c2..2a9ce384bb9 100644
+--- a/common/ldb_msg.c
++++ b/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 a/include/ldb_module.h b/include/ldb_module.h
+index 8c1e5ee7936..4c7c85a17f0 100644
+--- a/include/ldb_module.h
++++ b/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.35.0
+
+
+From e71ad28101f0703a80873b11459d0c9cef8fad5a Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 16 Feb 2022 12:35:13 +1300
+Subject: [PATCH 11/18] 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 at catalyst.net.nz>
+---
+ common/ldb_msg.c   | 52 ++++++++++++++++++++++++++++++++------
+ include/ldb.h      |  6 +++++
+ .../source4/dsdb/common/util.c | 20 +++++----------
+ 3 files changed, 56 insertions(+), 22 deletions(-)
+
+diff --git a/common/ldb_msg.c b/common/ldb_msg.c
+index 2a9ce384bb9..44d3b29e9a7 100644
+--- a/common/ldb_msg.c
++++ b/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 a/include/ldb.h b/include/ldb.h
+index f5f02c9a344..45b91cd5810 100644
+--- a/include/ldb.h
++++ b/include/ldb.h
+@@ -1980,6 +1980,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
+ */
+-- 
+2.35.0
+
+
+From a5d537e7b8b0ce292dcfe2093a54becaba6c3464 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 16 Feb 2022 16:30:03 +1300
+Subject: [PATCH 12/18] 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 at catalyst.net.nz>
+---
+ common/ldb_msg.c | 165 ++++++++++++++++++++++++++++++++++++++-
+ include/ldb.h    |  24 ++++++
+ 2 files changed, 185 insertions(+), 4 deletions(-)
+
+diff --git a/common/ldb_msg.c b/common/ldb_msg.c
+index 44d3b29e9a7..9cd7998e21c 100644
+--- a/common/ldb_msg.c
++++ b/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 a/include/ldb.h b/include/ldb.h
+index 45b91cd5810..0c3dbc3f22d 100644
+--- a/include/ldb.h
++++ b/include/ldb.h
+@@ -2001,12 +2001,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.35.0
+
+
+From 884513d579ecb858e9fce5677d7314e82fcee4f2 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Mon, 21 Feb 2022 16:27:37 +1300
+Subject: [PATCH 13/18] 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 at catalyst.net.nz>
+---
+ ldb_map/ldb_map.c                     |   5 +-
+ ldb_map/ldb_map_inbound.c             |   9 +-
+ 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 a/ldb_map/ldb_map.c b/ldb_map/ldb_map.c
+index b453dff80d2..c7b0c228631 100644
+--- a/ldb_map/ldb_map.c
++++ b/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 a/ldb_map/ldb_map_inbound.c b/ldb_map/ldb_map_inbound.c
+index 324295737da..50b9427c26c 100644
+--- a/ldb_map/ldb_map_inbound.c
++++ b/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 a/modules/rdn_name.c b/modules/rdn_name.c
+index 25cffe07591..3cb62bf567b 100644
+--- a/modules/rdn_name.c
++++ b/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);
+ 		}
+-- 
+2.35.0
+
+
+From 11a8dcfd4802a14e33d5d704dce1c1216029eebe Mon Sep 17 00:00:00 2001
+From: Andrew Bartlett <abartlet at samba.org>
+Date: Tue, 14 Jun 2022 15:43:26 +1200
+Subject: [PATCH 14/18] CVE-2022-32746 ldb: Release LDB 2.2.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 at samba.org>
+---
+ ABI/ldb-2.2.4.sigs        | 291 ++++++++++++++++++++++++++++++
+ ABI/pyldb-util-2.2.4.sigs |   3 +
+ wscript                   |   2 +-
+ 3 files changed, 295 insertions(+), 1 deletion(-)
+ create mode 100644 ABI/ldb-2.2.4.sigs
+ create mode 100644 ABI/pyldb-util-2.2.4.sigs
+
+diff --git a/ABI/ldb-2.2.4.sigs b/ABI/ldb-2.2.4.sigs
+new file mode 100644
+index 00000000000..40388d9e330
+--- /dev/null
++++ b/ABI/ldb-2.2.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 a/ABI/pyldb-util-2.2.4.sigs b/ABI/pyldb-util-2.2.4.sigs
+new file mode 100644
+index 00000000000..164a806b2ff
+--- /dev/null
++++ b/ABI/pyldb-util-2.2.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 *)
+diff --git a/wscript b/wscript
+index 57dfdd6fe6b..216cd70f9fa 100644
+--- a/wscript
++++ b/wscript
+@@ -2,7 +2,7 @@
+ 
+ APPNAME = 'ldb'
+ # For Samba 4.13.x
+-VERSION = '2.2.3'
++VERSION = '2.2.4'
+ 
+ import sys, os
+ 
+-- 
+2.35.0
+
diff -Nru ldb-2.2.3/debian/patches/series ldb-2.2.3/debian/patches/series
--- ldb-2.2.3/debian/patches/series	2021-11-04 22:08:35.000000000 +0300
+++ ldb-2.2.3/debian/patches/series	2022-08-01 17:18:51.000000000 +0300
@@ -4,3 +4,4 @@
 Fix-FTBFS-Increase-the-over-estimation-for-sparse-fi.patch
 Skip-ldb_lmdb_free_list_test-on-ppc64el-ppc64-and-sp.patch
 Skip_failing_tests.diff
+ldb-memory-bug-15096-CVE-2022-32745-4.13-v3.patch
diff -Nru ldb-2.2.3/debian/python3-ldb.symbols.in ldb-2.2.3/debian/python3-ldb.symbols.in
--- ldb-2.2.3/debian/python3-ldb.symbols.in	2021-11-04 22:03:57.000000000 +0300
+++ ldb-2.2.3/debian/python3-ldb.symbols.in	2022-08-01 17:18:51.000000000 +0300
@@ -1,6 +1,6 @@
 #!/usr/bin/dh-exec
 libpyldb-util${DEB_PY3_EXTENSION_SUFFIX}.2 python3-ldb #MINVER#
- PYLDB_UTIL${DEB_PY3_EXTENSION_UPCASE}_2.2.3 at PYLDB_UTIL${DEB_PY3_EXTENSION_UPCASE}_2.2.3 2:2.2.3
+ PYLDB_UTIL${DEB_PY3_EXTENSION_UPCASE}_2.2.4 at PYLDB_UTIL${DEB_PY3_EXTENSION_UPCASE}_2.2.4 2:2.2.3-2~deb11u2~
 #include "python3-ldb.symbols.common" PYLDB_UTIL_1.1.2 at PYLDB_UTIL_1.1.2 2:2.0.7
  PYLDB_UTIL_1.1.2 at PYLDB_UTIL_1.1.2 2:2.2.0
  PYLDB_UTIL_1.1.3 at PYLDB_UTIL_1.1.3 2:2.0.7
@@ -61,6 +61,7 @@
  PYLDB_UTIL_2.2.1 at PYLDB_UTIL_2.2.1 2:2.2.1
  PYLDB_UTIL_2.2.2 at PYLDB_UTIL_2.2.2 2:2.2.2
  PYLDB_UTIL_2.2.3 at PYLDB_UTIL_2.2.3 2:2.2.3
+ PYLDB_UTIL_2.2.4 at PYLDB_UTIL_2.2.4 2:2.2.3-2~deb11u2~
  pyldb_Dn_FromDn at PYLDB_UTIL_1.1.2 2:2.0.7
  pyldb_Object_AsDn at PYLDB_UTIL_1.1.2 2:2.0.7
  pyldb_check_type at PYLDB_UTIL_2.1.0 2:2.1.0
-------------- next part --------------
diff -Nru samba-4.13.13+dfsg/debian/changelog samba-4.13.13+dfsg/debian/changelog
--- samba-4.13.13+dfsg/debian/changelog	2022-05-28 22:52:59.000000000 +0300
+++ samba-4.13.13+dfsg/debian/changelog	2022-08-01 17:48:14.000000000 +0300
@@ -1,3 +1,31 @@
+samba (2:4.13.13+dfsg-1~deb11u5) stable-security; urgency=medium
+
+  * 3 patches:
+    - CVE-2022-32742-bug-15085-4.13.patch
+    - kpasswd_bugs_v15_4-13.patch
+    - ldb-memory-bug-15096-4.13-v3.patch
+    fixing:
+    o CVE-2022-2031: Samba AD users can bypass certain restrictions associated
+      with changing passwords.
+      https://www.samba.org/samba/security/CVE-2022-2031.html
+    o CVE-2022-32742: Server memory information leak via SMB1.
+      https://www.samba.org/samba/security/CVE-2022-32742.html
+    o CVE-2022-32744: Samba AD users can forge password change requests
+      for any user.
+      https://www.samba.org/samba/security/CVE-2022-32744.html
+    o CVE-2022-32745: Samba AD users can crash the server process with an LDAP
+      add or modify request.
+      https://www.samba.org/samba/security/CVE-2022-32745.html
+    o CVE-2022-32746: Samba AD users can induce a use-after-free in the server
+      process with an LDAP add or modify request.
+      https://www.samba.org/samba/security/CVE-2022-32746.html
+   * Closes: #1016449, CVE-2022-2031 CVE-2022-32742, CVE-2022-32744,
+     CVE-2022-32745, CVE-2022-32746
+   * Build-Depend on libldb-dev >= 2.2.3-2~deb11u2
+     (which includes the new symbols in libldb used by this update)
+
+ -- Michael Tokarev <mjt at tls.msk.ru>  Mon, 01 Aug 2022 17:48:14 +0300
+
 samba (2:4.13.13+dfsg-1~deb11u4) bullseye-proposed-updates; urgency=medium
 
   * fix the order of everything during build by exporting PYTHONHASHSEED=1
diff -Nru samba-4.13.13+dfsg/debian/control samba-4.13.13+dfsg/debian/control
--- samba-4.13.13+dfsg/debian/control	2022-05-28 22:52:17.000000000 +0300
+++ samba-4.13.13+dfsg/debian/control	2022-08-01 17:48:14.000000000 +0300
@@ -32,7 +32,7 @@
                libicu-dev,
                libjansson-dev,
                libldap2-dev,
-               libldb-dev (>= 2:2.2.3~),
+               libldb-dev (>= 2:2.2.3-2~deb11u2~),
                libncurses5-dev,
                libpam0g-dev,
                libparse-yapp-perl,
diff -Nru samba-4.13.13+dfsg/debian/patches/CVE-2022-32742-bug-15085-4.13.patch samba-4.13.13+dfsg/debian/patches/CVE-2022-32742-bug-15085-4.13.patch
--- samba-4.13.13+dfsg/debian/patches/CVE-2022-32742-bug-15085-4.13.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.13.13+dfsg/debian/patches/CVE-2022-32742-bug-15085-4.13.patch	2022-08-01 17:48:14.000000000 +0300
@@ -0,0 +1,198 @@
+From 89cbbcbc9b59e64c04fea0f965de16428847fc92 Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra at samba.org>
+Date: Tue, 7 Jun 2022 09:40:45 -0700
+Subject: [PATCH 1/2] 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 at samba.org>
+Reviewed-by: David Disseldorp <ddiss at samba.org>
+---
+ source4/torture/raw/write.c    | 89 ++++++++++++++++++++++++++++++++++
+ 1 file changed, 89 insertions(+)
+ create mode 100644 selftest/knownfail.d/bad-write
+
+diff --git a/source4/torture/raw/write.c b/source4/torture/raw/write.c
+index 0a2f50f425b..661485bb548 100644
+--- a/source4/torture/raw/write.c
++++ b/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.35.0
+
+
+From 31337a4b005685ae68d44fbd01ec20e5ffaea974 Mon Sep 17 00:00:00 2001
+From: Jeremy Allison <jra at samba.org>
+Date: Wed, 8 Jun 2022 13:50:51 -0700
+Subject: [PATCH 2/2] 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 at samba.org>
+Reviewed-by: David Disseldorp <ddiss at samba.org>
+---
+ source3/include/smb_macros.h   | 2 +-
+ source3/smbd/reply.c           | 4 ++--
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/source3/include/smb_macros.h b/source3/include/smb_macros.h
+index 68abe83dcbc..aa6de2a56aa 100644
+--- a/source3/include/smb_macros.h
++++ b/source3/include/smb_macros.h
+@@ -148,7 +148,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 a/source3/smbd/reply.c b/source3/smbd/reply.c
+index a0da2910350..bdcf3dce66a 100644
+--- a/source3/smbd/reply.c
++++ b/source3/smbd/reply.c
+@@ -415,7 +415,7 @@ size_t srvstr_get_path_req_wcard(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;
+ 	}
+@@ -464,7 +464,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.35.0
+
diff -Nru samba-4.13.13+dfsg/debian/patches/kpasswd_bugs_v15_4-13.patch samba-4.13.13+dfsg/debian/patches/kpasswd_bugs_v15_4-13.patch
--- samba-4.13.13+dfsg/debian/patches/kpasswd_bugs_v15_4-13.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.13.13+dfsg/debian/patches/kpasswd_bugs_v15_4-13.patch	2022-08-01 17:48:14.000000000 +0300
@@ -0,0 +1,11325 @@
+From c9f079814a05e6ce41d39139b2075f77a8445f19 Mon Sep 17 00:00:00 2001
+From: Isaac Boukris <iboukris at gmail.com>
+Date: Sat, 19 Sep 2020 14:16:20 +0200
+Subject: [PATCH 01/79] s4:mit-kdb: Force canonicalization for looking up
+ principals
+
+See also
+https://github.com/krb5/krb5/commit/ac8865a22138ab0c657208c41be8fd6bc7968148
+
+Pair-Programmed-With: Andreas Schneider <asn at samba.org>
+Signed-off-by: Isaac Boukris <iboukris at gmail.com>
+Signed-off-by: Andreas Schneider <asn at samba.org>
+Reviewed-by: Alexander Bokovoy <ab at samba.org>
+
+Autobuild-User(master): Andreas Schneider <asn at cryptomilk.org>
+Autobuild-Date(master): Mon Nov 29 09:32:26 UTC 2021 on sn-devel-184
+
+(cherry picked from commit 90febd2a33b88af49af595fe0e995d6ba0f33a1b)
+
+[jsutton at 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 a/source4/heimdal/lib/hdb/hdb.h b/source4/heimdal/lib/hdb/hdb.h
+index 5ef9d9565f3..dafaffc6c2d 100644
+--- a/source4/heimdal/lib/hdb/hdb.h
++++ b/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 a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
+index 0f19e8d1c93..3a480efe2e0 100644
+--- a/source4/kdc/db-glue.c
++++ b/source4/kdc/db-glue.c
+@@ -920,11 +920,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 a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c
+index e015c5a52db..c2a604045d9 100644
+--- a/source4/kdc/mit_samba.c
++++ b/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 a/source4/kdc/sdb.h b/source4/kdc/sdb.h
+index dbebb26ad0b..5b52d620f4f 100644
+--- a/source4/kdc/sdb.h
++++ b/source4/kdc/sdb.h
+@@ -114,6 +114,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.35.0
+
+
+From 8aacc45d6fe840d18abdcbeaa93c969373ce5259 Mon Sep 17 00:00:00 2001
+From: Andreas Schneider <asn at samba.org>
+Date: Tue, 21 Dec 2021 12:17:11 +0100
+Subject: [PATCH 02/79] s4:kdc: Also cannoicalize krbtgt principals when
+ enforcing canonicalization
+
+Signed-off-by: Andreas Schneider <asn at samba.org>
+Reviewed-by: Stefan Metzmacher <metze at samba.org>
+(cherry picked from commit f1ec950aeb47283a504018bafa21f54c3282e70c)
+---
+ source4/kdc/db-glue.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
+index 3a480efe2e0..982eb1e2792 100644
+--- a/source4/kdc/db-glue.c
++++ b/source4/kdc/db-glue.c
+@@ -883,7 +883,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.35.0
+
+
+From 23d236c123154bb6735d38d7fc072556e6589fd2 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:30:38 +1300
+Subject: [PATCH 03/79] selftest: Check received LDB error code when
+ STRICT_CHECKING=0
+
+We were instead only checking the expected error.
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit ad4d6fb01fd8083e68f07c427af8932574810cdc)
+---
+ source4/dsdb/tests/python/priv_attrs.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/source4/dsdb/tests/python/priv_attrs.py b/source4/dsdb/tests/python/priv_attrs.py
+index aa35dcc1317..4dfdfb9cbb8 100644
+--- a/source4/dsdb/tests/python/priv_attrs.py
++++ b/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.35.0
+
+
+From 223487ebbcf5ace1950f58b42bf21def205580c6 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:53:18 +1300
+Subject: [PATCH 04/79] tests/krb5: Remove unused variable
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 57b1b76154d699b9d70ad04fa5e94c4b30f0e4bf)
+---
+ python/samba/tests/krb5/raw_testcase.py | 2 --
+ 1 file changed, 2 deletions(-)
+
+diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py
+index 42f2e94f5aa..36a6134e6c9 100644
+--- a/python/samba/tests/krb5/raw_testcase.py
++++ b/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.35.0
+
+
+From 20c71a34b1f62527539c910978a2931d3709f980 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:34:11 +1300
+Subject: [PATCH 05/79] 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 at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/python/samba/tests/krb5/as_req_tests.py b/python/samba/tests/krb5/as_req_tests.py
+index 08081928363..315720f85d6 100755
+--- a/python/samba/tests/krb5/as_req_tests.py
++++ b/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
+ 
+- at 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
+ 
+ 
++ at 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 a/python/samba/tests/krb5/salt_tests.py b/python/samba/tests/krb5/salt_tests.py
+index ecbf618e40e..db777f8b7bc 100755
+--- a/python/samba/tests/krb5/salt_tests.py
++++ b/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.35.0
+
+
+From 8ae8f0c512b184be4eb246e6a60409f579ee1458 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 16:02:00 +1300
+Subject: [PATCH 06/79] tests/krb5: Run test_rpc against member server
+
+We were instead always running against the DC.
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 167bd2070483004cd0b9a96ffb40ea73c6ddf579)
+---
+ python/samba/tests/krb5/test_rpc.py | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/python/samba/tests/krb5/test_rpc.py b/python/samba/tests/krb5/test_rpc.py
+index 03c125f518a..2d483986e83 100755
+--- a/python/samba/tests/krb5/test_rpc.py
++++ b/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.35.0
+
+
+From 88a416dccfaebffbc465801f0e02d040ede37ea2 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:37:35 +1300
+Subject: [PATCH 07/79] tests/krb5: Allow PasswordKey_create() to use s2kparams
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit a560c2e9ad8abb824d1805c86c656943745f81eb)
+---
+ python/samba/tests/krb5/raw_testcase.py | 9 ++++++---
+ 1 file changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py
+index 36a6134e6c9..da3f69c79c6 100644
+--- a/python/samba/tests/krb5/raw_testcase.py
++++ b/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.35.0
+
+
+From bf8da5cb671e2fcb7d25c3557fd501a8ddba6156 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:40:35 +1300
+Subject: [PATCH 08/79] tests/krb5: Split out methods to create renewable or
+ invalid tickets
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index abac5a47a56..0578969ba69 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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.35.0
+
+
+From 2823c4537c1adaec4992a026c20032ca96d6984d Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 11:52:31 +1300
+Subject: [PATCH 09/79] tests/krb5: Adjust error codes to better match Windows
+ with PacRequestorEnforcement=2
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit d95705172bcf6fe24817800a4c0009e9cc8be595)
+
+[jsutton at 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 a/python/samba/tests/krb5/alias_tests.py b/python/samba/tests/krb5/alias_tests.py
+index 60213845a44..1f63775c189 100755
+--- a/python/samba/tests/krb5/alias_tests.py
++++ b/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 a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index 0578969ba69..7ea15f0fbab 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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 a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py b/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py
+index 0aa3309b814..e6b90d3e16a 100755
+--- a/python/samba/tests/krb5/ms_kile_client_principal_lookup_tests.py
++++ b/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 a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py
+index a80a7b3427e..5f37525f393 100755
+--- a/python/samba/tests/krb5/s4u_tests.py
++++ b/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 a/python/samba/tests/krb5/test_rpc.py b/python/samba/tests/krb5/test_rpc.py
+index 2d483986e83..5a3c7339cea 100755
+--- a/python/samba/tests/krb5/test_rpc.py
++++ b/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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 7eba899966e..1b7e159c381 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 8cd36fe2d96..cc12499bb50 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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.35.0
+
+
+From 4a39eefd4d4868849f1ce84b2540870ac2c02237 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 12:04:36 +1300
+Subject: [PATCH 10/79] 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 at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index 7ea15f0fbab..6160ef649e8 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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.35.0
+
+
+From 429278045afcd140f500373c66fdf217098932a4 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 30 Nov 2021 09:26:40 +1300
+Subject: [PATCH 11/79] tests/krb5: Add tests for invalid TGTs
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 7574ba9f580fca552b80532a49d00e657fbdf4fd)
+
+[jsutton at 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 a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index 6160ef649e8..f5f091610ac 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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 a/python/samba/tests/krb5/rfc4120_constants.py b/python/samba/tests/krb5/rfc4120_constants.py
+index 5251e291fde..a9fdc5735dd 100644
+--- a/python/samba/tests/krb5/rfc4120_constants.py
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index cc12499bb50..3aacec00870 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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.35.0
+
+
+From d102542258c875d314b08435d98258657e3e3f7a Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 12:10:45 +1300
+Subject: [PATCH 12/79] tests/krb5: Add tests for TGS requests with a non-TGT
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index f5f091610ac..52297c963e8 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 3aacec00870..98e8a34cd5f 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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.35.0
+
+
+From 8dada61cd6a72d9f8b9535a874d4cf18a70db798 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 12:09:18 +1300
+Subject: [PATCH 13/79] tests/krb5: Add TGS-REQ tests with FAST
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index 52297c963e8..99a91528fa8 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 1b7e159c381..61de06659be 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 98e8a34cd5f..3e19ee6c8b9 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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.35.0
+
+
+From ed5c0f6692908c70eabcd08f5988a1f95e29ead1 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 12:37:08 +1300
+Subject: [PATCH 14/79] 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 at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit ebc9137cee94dee9dcf0e47d5bc0dc83de7aaaa1)
+
+[jsutton at 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 a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index 99a91528fa8..f14439a4ab5 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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 a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py
+index da3f69c79c6..14e655313fc 100644
+--- a/python/samba/tests/krb5/raw_testcase.py
++++ b/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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 61de06659be..294e06027b1 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 3e19ee6c8b9..6c74657e87d 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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
+diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
+index 5c949026674..9aafdc202fb 100755
+--- a/source4/selftest/tests.py
++++ b/source4/selftest/tests.py
+@@ -790,12 +790,14 @@ planoldpythontestsuite("ad_dc:local", "samba.tests.dckeytab", extra_args=['-U"$U
+ have_fast_support = int('SAMBA_USES_MITKDC' in config_hash)
+ tkt_sig_support = int('SAMBA4_USES_HEIMDAL' in config_hash)
+ expect_pac = int('SAMBA4_USES_HEIMDAL' in config_hash)
++extra_pac_buffers = 0
+ planoldpythontestsuite("none", "samba.tests.krb5.kcrypto")
+ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.simple_tests",
+                        environ={'SERVICE_USERNAME':'$SERVER',
+                                 'FAST_SUPPORT': have_fast_support,
+                                 'TKT_SIG_SUPPORT': tkt_sig_support,
+-                                'EXPECT_PAC': expect_pac})
++                                'EXPECT_PAC': expect_pac,
++                                'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers})
+ planoldpythontestsuite("ad_dc_default:local", "samba.tests.krb5.s4u_tests",
+                        environ={'ADMIN_USERNAME':'$USERNAME',
+                                 'ADMIN_PASSWORD':'$PASSWORD',
+@@ -803,19 +805,22 @@ planoldpythontestsuite("ad_dc_default:local", "samba.tests.krb5.s4u_tests",
+                                 'STRICT_CHECKING':'0',
+                                 'FAST_SUPPORT': have_fast_support,
+                                 'TKT_SIG_SUPPORT': tkt_sig_support,
+-                                'EXPECT_PAC': expect_pac})
++                                'EXPECT_PAC': expect_pac,
++                                'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers})
+ planoldpythontestsuite("rodc:local", "samba.tests.krb5.rodc_tests",
+                        environ={'ADMIN_USERNAME':'$USERNAME',
+                                 'ADMIN_PASSWORD':'$PASSWORD',
+                                 'STRICT_CHECKING':'0',
+                                 'FAST_SUPPORT': have_fast_support,
+                                 'TKT_SIG_SUPPORT': tkt_sig_support,
+-                                'EXPECT_PAC': expect_pac})
++                                'EXPECT_PAC': expect_pac,
++                                'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers})
+ 
+ planoldpythontestsuite("fl2008r2dc:local", "samba.tests.krb5.xrealm_tests",
+                        environ={'FAST_SUPPORT': have_fast_support,
+                                 'TKT_SIG_SUPPORT': tkt_sig_support,
+-                                'EXPECT_PAC': expect_pac})
++                                'EXPECT_PAC': expect_pac,
++                                'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers})
+ 
+ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ccache",
+                        environ={
+@@ -824,7 +829,8 @@ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ccache",
+                            'STRICT_CHECKING': '0',
+                            'FAST_SUPPORT': have_fast_support,
+                            'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac
++                           'EXPECT_PAC': expect_pac,
++                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+                        })
+ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ldap",
+                        environ={
+@@ -833,7 +839,8 @@ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ldap",
+                            'STRICT_CHECKING': '0',
+                            'FAST_SUPPORT': have_fast_support,
+                            'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac
++                           'EXPECT_PAC': expect_pac,
++                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+                        })
+ for env in ['ad_dc_default', 'ad_member']:
+     planoldpythontestsuite(env, "samba.tests.krb5.test_rpc",
+@@ -843,7 +850,8 @@ for env in ['ad_dc_default', 'ad_member']:
+                                'STRICT_CHECKING': '0',
+                                'FAST_SUPPORT': have_fast_support,
+                                'TKT_SIG_SUPPORT': tkt_sig_support,
+-                               'EXPECT_PAC': expect_pac
++                               'EXPECT_PAC': expect_pac,
++                               'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+                            })
+ planoldpythontestsuite("ad_dc_smb1", "samba.tests.krb5.test_smb",
+                        environ={
+@@ -852,7 +860,8 @@ planoldpythontestsuite("ad_dc_smb1", "samba.tests.krb5.test_smb",
+                            'STRICT_CHECKING': '0',
+                            'FAST_SUPPORT': have_fast_support,
+                            'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac
++                           'EXPECT_PAC': expect_pac,
++                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+                        })
+ planoldpythontestsuite("ad_member_idmap_nss:local",
+                        "samba.tests.krb5.test_min_domain_uid",
+@@ -875,7 +884,8 @@ planoldpythontestsuite("ad_member_idmap_nss:local",
+                            'STRICT_CHECKING': '0',
+                            'FAST_SUPPORT': have_fast_support,
+                            'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac
++                           'EXPECT_PAC': expect_pac,
++                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+                        })
+ 
+ for env in ["ad_dc", smbv1_disabled_testenv]:
+@@ -1445,7 +1455,8 @@ for env in ["fl2008r2dc", "fl2003dc"]:
+                                'STRICT_CHECKING': '0',
+                                'FAST_SUPPORT': have_fast_support,
+                                'TKT_SIG_SUPPORT': tkt_sig_support,
+-                               'EXPECT_PAC': expect_pac
++                               'EXPECT_PAC': expect_pac,
++                               'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+                            })
+ 
+ planoldpythontestsuite('fl2008r2dc', 'samba.tests.krb5.salt_tests',
+@@ -1455,7 +1466,8 @@ planoldpythontestsuite('fl2008r2dc', 'samba.tests.krb5.salt_tests',
+                            'STRICT_CHECKING': '0',
+                            'FAST_SUPPORT': have_fast_support,
+                            'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac
++                           'EXPECT_PAC': expect_pac,
++                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+                        })
+ 
+ for env in ["rodc", "promoted_dc", "fl2000dc", "fl2008r2dc"]:
+@@ -1478,7 +1490,8 @@ planpythontestsuite("ad_dc", "samba.tests.krb5.as_canonicalization_tests",
+                            'ADMIN_PASSWORD': '$PASSWORD',
+                            'FAST_SUPPORT': have_fast_support,
+                            'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac
++                           'EXPECT_PAC': expect_pac,
++                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+                        })
+ planpythontestsuite("ad_dc", "samba.tests.krb5.compatability_tests",
+                     environ={
+@@ -1487,12 +1500,14 @@ planpythontestsuite("ad_dc", "samba.tests.krb5.compatability_tests",
+                         'STRICT_CHECKING': '0',
+                         'FAST_SUPPORT': have_fast_support,
+                         'TKT_SIG_SUPPORT': tkt_sig_support,
+-                        'EXPECT_PAC': expect_pac
++                        'EXPECT_PAC': expect_pac,
++                        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+                     })
+ planpythontestsuite("ad_dc", "samba.tests.krb5.kdc_tests",
+                     environ={'FAST_SUPPORT': have_fast_support,
+                              'TKT_SIG_SUPPORT': tkt_sig_support,
+-                             'EXPECT_PAC': expect_pac})
++                             'EXPECT_PAC': expect_pac,
++                             'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers})
+ planpythontestsuite(
+     "ad_dc",
+     "samba.tests.krb5.kdc_tgs_tests",
+@@ -1502,7 +1517,8 @@ planpythontestsuite(
+         'STRICT_CHECKING': '0',
+         'FAST_SUPPORT': have_fast_support,
+         'TKT_SIG_SUPPORT': tkt_sig_support,
+-        'EXPECT_PAC': expect_pac
++        'EXPECT_PAC': expect_pac,
++        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+     })
+ planpythontestsuite(
+     "ad_dc",
+@@ -1513,7 +1529,8 @@ planpythontestsuite(
+         'STRICT_CHECKING': '0',
+         'FAST_SUPPORT': have_fast_support,
+         'TKT_SIG_SUPPORT': tkt_sig_support,
+-        'EXPECT_PAC': expect_pac
++        'EXPECT_PAC': expect_pac,
++        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+     })
+ planpythontestsuite(
+     "ad_dc",
+@@ -1524,7 +1541,8 @@ planpythontestsuite(
+         'STRICT_CHECKING': '0',
+         'FAST_SUPPORT': have_fast_support,
+         'TKT_SIG_SUPPORT': tkt_sig_support,
+-        'EXPECT_PAC': expect_pac
++        'EXPECT_PAC': expect_pac,
++        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+     })
+ planpythontestsuite(
+     "ad_dc",
+@@ -1535,7 +1553,8 @@ planpythontestsuite(
+         'STRICT_CHECKING': '0',
+         'FAST_SUPPORT': have_fast_support,
+         'TKT_SIG_SUPPORT': tkt_sig_support,
+-        'EXPECT_PAC': expect_pac
++        'EXPECT_PAC': expect_pac,
++        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+     })
+ planpythontestsuite(
+     "ad_dc",
+@@ -1546,7 +1565,8 @@ planpythontestsuite(
+         'STRICT_CHECKING': '0',
+         'FAST_SUPPORT': have_fast_support,
+         'TKT_SIG_SUPPORT': tkt_sig_support,
+-        'EXPECT_PAC': expect_pac
++        'EXPECT_PAC': expect_pac,
++        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+     })
+ 
+ for env in [
+-- 
+2.35.0
+
+
+From 8ffdb2b993c01bb20907e145b9d19f826b975d31 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 13:10:52 +1300
+Subject: [PATCH 15/79] tests/krb5: Add tests for validation with requester SID
+ PAC buffer
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index f14439a4ab5..50079a1710c 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 294e06027b1..f7c5feda872 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 6c74657e87d..ff287e6cd9d 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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.35.0
+
+
+From 35c12e042376cb77cd48787313ca63c1435d3358 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 15:32:32 +1300
+Subject: [PATCH 16/79] tests/krb5: Add comments for tests that fail against
+ Windows
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 749349efab9b401d33a4fc286473a924364a41c9)
+---
+ python/samba/tests/krb5/kdc_tgs_tests.py | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index 50079a1710c..ecc38538e61 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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.35.0
+
+
+From 5c46bdcf0e3a8d61c91ceded681eaa64debbb6ac Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 18 Nov 2021 13:14:51 +1300
+Subject: [PATCH 17/79] 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 at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 11fb9476ad3c09415d12b3cdf7934c293cbefcb2)
+---
+ source4/heimdal/kdc/krb5tgs.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c
+index fb2ef8230c9..cde68b41714 100644
+--- a/source4/heimdal/kdc/krb5tgs.c
++++ b/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.35.0
+
+
+From 3b2257d1b666789be95f76ce9973720c86eceb0d Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 18 Nov 2021 16:22:34 +1300
+Subject: [PATCH 18/79] s4:torture: Fix typo
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 9cfb88ba04818b5e9cec3c96422e8e4a3080d490)
+---
+ source4/torture/krb5/kdc-canon-heimdal.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/source4/torture/krb5/kdc-canon-heimdal.c b/source4/torture/krb5/kdc-canon-heimdal.c
+index 9e0808b134c..4b546147ac4 100644
+--- a/source4/torture/krb5/kdc-canon-heimdal.c
++++ b/source4/torture/krb5/kdc-canon-heimdal.c
+@@ -260,7 +260,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.35.0
+
+
+From 67ba13f27a536fad5d3fce39275977dc4a88becb Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 20:41:34 +1300
+Subject: [PATCH 19/79] heimdal:kdc: Adjust no-PAC error code to match Windows
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index f7c5feda872..9ff85fe18fc 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c
+index cde68b41714..6c5c51aa448 100644
+--- a/source4/heimdal/kdc/krb5tgs.c
++++ b/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.35.0
+
+
+From 0fe40236bfa8716fa4c494f15e101a2a80abf6f7 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 20:41:45 +1300
+Subject: [PATCH 20/79] kdc: Adjust SID mismatch error code to match Windows
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 9ff85fe18fc..bc644587319 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index ff287e6cd9d..c6dc1285837 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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 a/source4/kdc/pac-glue.c b/source4/kdc/pac-glue.c
+index e0e483662c0..2a96a683cd9 100644
+--- a/source4/kdc/pac-glue.c
++++ b/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.35.0
+
+
+From 7a062e486e6103cab94a1f7b55ece17c2edeb436 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 25 Nov 2021 10:05:17 +1300
+Subject: [PATCH 21/79] tests/krb5: Add test for S4U2Self with wrong sname
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py
+index 5f37525f393..2953766ef21 100755
+--- a/python/samba/tests/krb5/s4u_tests.py
++++ b/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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index bc644587319..483145f1473 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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.35.0
+
+
+From 8d343a6da01239ca6f8eacf4a8c8afcdbfa3bbbd Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 23 Nov 2021 20:00:07 +1300
+Subject: [PATCH 22/79] kdc: Match Windows error code for mismatching sname
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 483145f1473..981d7894158 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
+index 982eb1e2792..d60560d79bc 100644
+--- a/source4/kdc/db-glue.c
++++ b/source4/kdc/db-glue.c
+@@ -2562,7 +2562,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.35.0
+
+
+From 4af6e88ab489c2f56fd17beb4a95be93024d857d Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 23 Nov 2021 20:15:41 +1300
+Subject: [PATCH 23/79] kdc: Always add the PAC if the header TGT is from an
+ RODC
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 981d7894158..94a4509f45a 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c
+index ecd182702c3..8c3ce71529c 100644
+--- a/source4/kdc/wdc-samba4.c
++++ b/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.35.0
+
+
+From 4e9c780904eb09fda0ac984e10a2ef309334c66c Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 25 Nov 2021 10:32:44 +1300
+Subject: [PATCH 24/79] tests/krb5: Add tests for renewal and validation of
+ RODC TGTs with PAC requests
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index ecc38538e61..2923d53772a 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 94a4509f45a..2de898e73c2 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index c6dc1285837..73e64145e42 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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.35.0
+
+
+From 23873a1776011f5d3f85f6f9f350670fecfdd2c3 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 25 Nov 2021 13:24:57 +1300
+Subject: [PATCH 25/79] 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 at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 2de898e73c2..65e4fee9510 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/torture/rpc/remote_pac.c b/source4/torture/rpc/remote_pac.c
+index c94decef5ce..14c23f674f1 100644
+--- a/source4/torture/rpc/remote_pac.c
++++ b/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.35.0
+
+
+From d9efb82edf073cc139c741e1091feaceb57db0fb Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 20:42:22 +1300
+Subject: [PATCH 26/79] kdc: Don't include extra PAC buffers in service tickets
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit 90025b6a4d250a15c0f988a9a9150ecfb63069ef)
+---
+ selftest/knownfail_heimdal_kdc | 42 ----------------------------------
+ source4/kdc/wdc-samba4.c       | 33 +++++++++++++++++---------
+ 2 files changed, 22 insertions(+), 53 deletions(-)
+
+diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 65e4fee9510..ea08cb44122 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c
+index 8c3ce71529c..17af76f4edb 100644
+--- a/source4/kdc/wdc-samba4.c
++++ b/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;
+-		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) {
++				/* just copy... */
++				break;
++			} else {
++				continue;
++			}
++		case PAC_TYPE_REQUESTER_SID:
++			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.35.0
+
+
+From f583378ac1c139f8a55ddb4fc75d4cf3f78405d6 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 25 Nov 2021 10:53:49 +1300
+Subject: [PATCH 27/79] 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 at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index ea08cb44122..5e94cb63d7a 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c
+index 17af76f4edb..713720bcb99 100644
+--- a/source4/kdc/wdc-samba4.c
++++ b/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.35.0
+
+
+From 6cb3b36af6a47ecf18f4533631a763d5896dae4c Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 25 Nov 2021 12:46:40 +1300
+Subject: [PATCH 28/79] tests/krb5: Add a test for S4U2Self with no
+ authorization data required
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py
+index 2953766ef21..6ec9af11423 100755
+--- a/python/samba/tests/krb5/s4u_tests.py
++++ b/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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 5e94cb63d7a..2025032a278 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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.35.0
+
+
+From e1c92e8e165edd6f169cccba49308bb2d18f2899 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 23 Nov 2021 17:30:50 +1300
+Subject: [PATCH 29/79] 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 at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 2025032a278..53cc8e6b6a2 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c
+index 6c5c51aa448..dc356b4daa5 100644
+--- a/source4/heimdal/kdc/krb5tgs.c
++++ b/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.35.0
+
+
+From cd8acd48820d5d04adb3548f9720a17411bf2648 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 25 Nov 2021 09:29:42 +1300
+Subject: [PATCH 30/79] selftest: Properly check extra PAC buffers with Heimdal
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+(cherry picked from commit ee4aa21c487fa80082a548b2e4f115a791e30340)
+
+[jsutton at samba.org Fixed conflicts]
+---
+ selftest/knownfail_heimdal_kdc | 12 ++++++++++++
+ source4/selftest/tests.py      |  2 +-
+ 2 files changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 53cc8e6b6a2..f71b95f306e 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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
+diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
+index 9aafdc202fb..94e8225c19e 100755
+--- a/source4/selftest/tests.py
++++ b/source4/selftest/tests.py
+@@ -790,7 +790,7 @@ planoldpythontestsuite("ad_dc:local", "samba.tests.dckeytab", extra_args=['-U"$U
+ have_fast_support = int('SAMBA_USES_MITKDC' in config_hash)
+ tkt_sig_support = int('SAMBA4_USES_HEIMDAL' in config_hash)
+ expect_pac = int('SAMBA4_USES_HEIMDAL' in config_hash)
+-extra_pac_buffers = 0
++extra_pac_buffers = int('SAMBA4_USES_HEIMDAL' in config_hash)
+ planoldpythontestsuite("none", "samba.tests.krb5.kcrypto")
+ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.simple_tests",
+                        environ={'SERVICE_USERNAME':'$SERVER',
+-- 
+2.35.0
+
+
+From 8f7d0388632eb334ffe93ef3798638fa96aec8c0 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 23 Nov 2021 19:38:35 +1300
+Subject: [PATCH 31/79] 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 at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index f71b95f306e..53cc8e6b6a2 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/heimdal/kdc/kerberos5.c b/source4/heimdal/kdc/kerberos5.c
+index c1d4cb1d4aa..c7651379244 100644
+--- a/source4/heimdal/kdc/kerberos5.c
++++ b/source4/heimdal/kdc/kerberos5.c
+@@ -1770,7 +1770,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 a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c
+index dc356b4daa5..38dba8493ae 100644
+--- a/source4/heimdal/kdc/krb5tgs.c
++++ b/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 a/source4/heimdal/kdc/windc.c b/source4/heimdal/kdc/windc.c
+index 93b973f576b..0a5ae5025ec 100644
+--- a/source4/heimdal/kdc/windc.c
++++ b/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 a/source4/heimdal/kdc/windc_plugin.h b/source4/heimdal/kdc/windc_plugin.h
+index c7f2bcb5ed9..d239d0260e7 100644
+--- a/source4/heimdal/kdc/windc_plugin.h
++++ b/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 a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c
+index 713720bcb99..b1d011c09a9 100644
+--- a/source4/kdc/wdc-samba4.c
++++ b/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.35.0
+
+
+From 05f14a06b9e3e78badb3629229c7ad41a73c8f0d Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 24 Nov 2021 20:41:54 +1300
+Subject: [PATCH 32/79] kdc: Require that PAC_REQUESTER_SID buffer is present
+ for TGTs
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+
+Autobuild-User(master): Andrew Bartlett <abartlet at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 53cc8e6b6a2..32465cb6042 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/kdc/wdc-samba4.c b/source4/kdc/wdc-samba4.c
+index b1d011c09a9..d7ce34fb3a9 100644
+--- a/source4/kdc/wdc-samba4.c
++++ b/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.35.0
+
+
+From 0c4c7eed52b49d658679f0e8a5d99e553b405732 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 7 Dec 2021 13:15:38 +1300
+Subject: [PATCH 33/79] kdc: Canonicalize realm for enterprise principals
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at samba.org>
+
+Autobuild-User(master): Andrew Bartlett <abartlet at 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 a/selftest/knownfail.d/kdc-enterprise b/selftest/knownfail.d/kdc-enterprise
+deleted file mode 100644
+index c9b6c98a2ee..00000000000
+--- a/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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 32465cb6042..424a8b81c38 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 73e64145e42..4d685af7140 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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 a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
+index d60560d79bc..c4ca2c3fcba 100644
+--- a/source4/kdc/db-glue.c
++++ b/source4/kdc/db-glue.c
+@@ -943,19 +943,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.35.0
+
+
+From a3cfb00497a68c9bc4eb6060cd859802793497a5 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 14 Dec 2021 19:16:00 +1300
+Subject: [PATCH 34/79] 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 at catalyst.net.nz>
+Reviewed-by: Andrew Bartlett <abartlet at 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 a/python/samba/tests/krb5/compatability_tests.py b/python/samba/tests/krb5/compatability_tests.py
+index ed2dc565b6d..65e9e3788d5 100755
+--- a/python/samba/tests/krb5/compatability_tests.py
++++ b/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 a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py
+index 6e96b982167..9506048ee2a 100644
+--- a/python/samba/tests/krb5/kdc_base_test.py
++++ b/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 a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py
+index 14e655313fc..a2241707d44 100644
+--- a/python/samba/tests/krb5/raw_testcase.py
++++ b/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 a/python/samba/tests/krb5/rodc_tests.py b/python/samba/tests/krb5/rodc_tests.py
+index 0e252d90262..83ee35d650a 100755
+--- a/python/samba/tests/krb5/rodc_tests.py
++++ b/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.35.0
+
+
+From 2cd223c8140af1524be8a95fe1e6678516423c34 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 8 Feb 2022 12:15:36 +1300
+Subject: [PATCH 35/79] tests/krb5: Add helper function to modify ticket flags
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Stefan Metzmacher <metze at 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 a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py
+index 9506048ee2a..58b87eab25b 100644
+--- a/python/samba/tests/krb5/kdc_base_test.py
++++ b/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 a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index 2923d53772a..8cd27dec2aa 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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 a/python/samba/tests/krb5/s4u_tests.py b/python/samba/tests/krb5/s4u_tests.py
+index 6ec9af11423..49dd89cd764 100755
+--- a/python/samba/tests/krb5/s4u_tests.py
++++ b/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.35.0
+
+
+From 9c19fa70a035110053aff625592dbc75700eca84 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Fri, 4 Mar 2022 16:57:27 +1300
+Subject: [PATCH 36/79] selftest: Simplify krb5 test environments
+
+It's not necessary to repeat the required environment variables for
+every test.
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+(cherry picked from commit e729606631b5bfaf7c4ad8c1e70697adf8274777)
+
+[jsutton at samba.org Fixed conflicts caused by missing check_cname,
+ check_padata and fast_support variables]
+
+[jsutton at samba.org Fixed conflicts]
+---
+ source4/selftest/tests.py | 191 +++++++-------------------------------
+ 1 file changed, 35 insertions(+), 156 deletions(-)
+
+diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
+index 94e8225c19e..0d560dc23a3 100755
+--- a/source4/selftest/tests.py
++++ b/source4/selftest/tests.py
+@@ -788,104 +788,57 @@ planoldpythontestsuite("ad_dc:local", "samba.tests.gpo", extra_args=['-U"$USERNA
+ planoldpythontestsuite("ad_dc:local", "samba.tests.dckeytab", extra_args=['-U"$USERNAME%$PASSWORD"'])
+ 
+ have_fast_support = int('SAMBA_USES_MITKDC' in config_hash)
++claims_support = 0
++compound_id_support = 0
+ tkt_sig_support = int('SAMBA4_USES_HEIMDAL' in config_hash)
+ expect_pac = int('SAMBA4_USES_HEIMDAL' in config_hash)
+ extra_pac_buffers = int('SAMBA4_USES_HEIMDAL' in config_hash)
++krb5_environ = {
++    'SERVICE_USERNAME': '$SERVER',
++    'ADMIN_USERNAME': '$DC_USERNAME',
++    'ADMIN_PASSWORD': '$DC_PASSWORD',
++    'FOR_USER': '$DC_USERNAME',
++    'STRICT_CHECKING':'0',
++    'FAST_SUPPORT': have_fast_support,
++    'CLAIMS_SUPPORT': claims_support,
++    'COMPOUND_ID_SUPPORT': compound_id_support,
++    'TKT_SIG_SUPPORT': tkt_sig_support,
++    'EXPECT_PAC': expect_pac,
++    'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers,
++}
+ planoldpythontestsuite("none", "samba.tests.krb5.kcrypto")
+ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.simple_tests",
+-                       environ={'SERVICE_USERNAME':'$SERVER',
+-                                'FAST_SUPPORT': have_fast_support,
+-                                'TKT_SIG_SUPPORT': tkt_sig_support,
+-                                'EXPECT_PAC': expect_pac,
+-                                'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers})
++                       environ=krb5_environ)
+ planoldpythontestsuite("ad_dc_default:local", "samba.tests.krb5.s4u_tests",
+-                       environ={'ADMIN_USERNAME':'$USERNAME',
+-                                'ADMIN_PASSWORD':'$PASSWORD',
+-                                'FOR_USER':'$USERNAME',
+-                                'STRICT_CHECKING':'0',
+-                                'FAST_SUPPORT': have_fast_support,
+-                                'TKT_SIG_SUPPORT': tkt_sig_support,
+-                                'EXPECT_PAC': expect_pac,
+-                                'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers})
++                       environ=krb5_environ)
+ planoldpythontestsuite("rodc:local", "samba.tests.krb5.rodc_tests",
+-                       environ={'ADMIN_USERNAME':'$USERNAME',
+-                                'ADMIN_PASSWORD':'$PASSWORD',
+-                                'STRICT_CHECKING':'0',
+-                                'FAST_SUPPORT': have_fast_support,
+-                                'TKT_SIG_SUPPORT': tkt_sig_support,
+-                                'EXPECT_PAC': expect_pac,
+-                                'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers})
++                       environ=krb5_environ)
+ 
+ planoldpythontestsuite("fl2008r2dc:local", "samba.tests.krb5.xrealm_tests",
+-                       environ={'FAST_SUPPORT': have_fast_support,
+-                                'TKT_SIG_SUPPORT': tkt_sig_support,
+-                                'EXPECT_PAC': expect_pac,
+-                                'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers})
++                       environ=krb5_environ)
+ 
+ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ccache",
+-                       environ={
+-                           'ADMIN_USERNAME': '$USERNAME',
+-                           'ADMIN_PASSWORD': '$PASSWORD',
+-                           'STRICT_CHECKING': '0',
+-                           'FAST_SUPPORT': have_fast_support,
+-                           'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac,
+-                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-                       })
++                       environ=krb5_environ)
+ planoldpythontestsuite("ad_dc_default", "samba.tests.krb5.test_ldap",
+-                       environ={
+-                           'ADMIN_USERNAME': '$USERNAME',
+-                           'ADMIN_PASSWORD': '$PASSWORD',
+-                           'STRICT_CHECKING': '0',
+-                           'FAST_SUPPORT': have_fast_support,
+-                           'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac,
+-                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-                       })
++                       environ=krb5_environ)
+ for env in ['ad_dc_default', 'ad_member']:
+     planoldpythontestsuite(env, "samba.tests.krb5.test_rpc",
+-                           environ={
+-                               'ADMIN_USERNAME': '$DC_USERNAME',
+-                               'ADMIN_PASSWORD': '$DC_PASSWORD',
+-                               'STRICT_CHECKING': '0',
+-                               'FAST_SUPPORT': have_fast_support,
+-                               'TKT_SIG_SUPPORT': tkt_sig_support,
+-                               'EXPECT_PAC': expect_pac,
+-                               'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-                           })
++                           environ=krb5_environ)
+ planoldpythontestsuite("ad_dc_smb1", "samba.tests.krb5.test_smb",
+-                       environ={
+-                           'ADMIN_USERNAME': '$USERNAME',
+-                           'ADMIN_PASSWORD': '$PASSWORD',
+-                           'STRICT_CHECKING': '0',
+-                           'FAST_SUPPORT': have_fast_support,
+-                           'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac,
+-                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-                       })
++                       environ=krb5_environ)
+ planoldpythontestsuite("ad_member_idmap_nss:local",
+                        "samba.tests.krb5.test_min_domain_uid",
+-                       environ={
+-                           'ADMIN_USERNAME': '$DC_USERNAME',
+-                           'ADMIN_PASSWORD': '$DC_PASSWORD',
+-                           'STRICT_CHECKING': '0'
+-                       })
++                       environ=krb5_environ)
+ planoldpythontestsuite("ad_member_idmap_nss:local",
+                        "samba.tests.krb5.test_idmap_nss",
+                        environ={
+-                           'ADMIN_USERNAME': '$DC_USERNAME',
+-                           'ADMIN_PASSWORD': '$DC_PASSWORD',
++                           **krb5_environ,
+                            'MAPPED_USERNAME': 'bob',
+                            'MAPPED_PASSWORD': 'Secret007',
+                            'UNMAPPED_USERNAME': 'jane',
+                            'UNMAPPED_PASSWORD': 'Secret007',
+                            'INVALID_USERNAME': 'joe',
+                            'INVALID_PASSWORD': 'Secret007',
+-                           'STRICT_CHECKING': '0',
+-                           'FAST_SUPPORT': have_fast_support,
+-                           'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac,
+-                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+                        })
+ 
+ for env in ["ad_dc", smbv1_disabled_testenv]:
+@@ -1449,26 +1402,10 @@ plansmbtorture4testsuite('krb5.kdc', env, ['ncacn_np:$SERVER_IP', "-k", "yes", '
+                          "samba4.krb5.kdc with account having identical UPN and SPN")
+ for env in ["fl2008r2dc", "fl2003dc"]:
+     planoldpythontestsuite(env, "samba.tests.krb5.as_req_tests",
+-                           environ={
+-                               'ADMIN_USERNAME': '$USERNAME',
+-                               'ADMIN_PASSWORD': '$PASSWORD',
+-                               'STRICT_CHECKING': '0',
+-                               'FAST_SUPPORT': have_fast_support,
+-                               'TKT_SIG_SUPPORT': tkt_sig_support,
+-                               'EXPECT_PAC': expect_pac,
+-                               'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-                           })
++                           environ=krb5_environ)
+ 
+ planoldpythontestsuite('fl2008r2dc', 'samba.tests.krb5.salt_tests',
+-                       environ={
+-                           'ADMIN_USERNAME': '$USERNAME',
+-                           'ADMIN_PASSWORD': '$PASSWORD',
+-                           'STRICT_CHECKING': '0',
+-                           'FAST_SUPPORT': have_fast_support,
+-                           'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac,
+-                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-                       })
++                       environ=krb5_environ)
+ 
+ for env in ["rodc", "promoted_dc", "fl2000dc", "fl2008r2dc"]:
+     if env == "rodc":
+@@ -1485,89 +1422,31 @@ for env in ["rodc", "promoted_dc", "fl2000dc", "fl2008r2dc"]:
+                              "samba4.krb5.kdc with machine account")
+ 
+ planpythontestsuite("ad_dc", "samba.tests.krb5.as_canonicalization_tests",
+-                       environ={
+-                           'ADMIN_USERNAME': '$USERNAME',
+-                           'ADMIN_PASSWORD': '$PASSWORD',
+-                           'FAST_SUPPORT': have_fast_support,
+-                           'TKT_SIG_SUPPORT': tkt_sig_support,
+-                           'EXPECT_PAC': expect_pac,
+-                           'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-                       })
++                    environ=krb5_environ)
+ planpythontestsuite("ad_dc", "samba.tests.krb5.compatability_tests",
+-                    environ={
+-                        'ADMIN_USERNAME': '$USERNAME',
+-                        'ADMIN_PASSWORD': '$PASSWORD',
+-                        'STRICT_CHECKING': '0',
+-                        'FAST_SUPPORT': have_fast_support,
+-                        'TKT_SIG_SUPPORT': tkt_sig_support,
+-                        'EXPECT_PAC': expect_pac,
+-                        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-                    })
++                    environ=krb5_environ)
+ planpythontestsuite("ad_dc", "samba.tests.krb5.kdc_tests",
+-                    environ={'FAST_SUPPORT': have_fast_support,
+-                             'TKT_SIG_SUPPORT': tkt_sig_support,
+-                             'EXPECT_PAC': expect_pac,
+-                             'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers})
++                    environ=krb5_environ)
+ planpythontestsuite(
+     "ad_dc",
+     "samba.tests.krb5.kdc_tgs_tests",
+-    environ={
+-        'ADMIN_USERNAME': '$USERNAME',
+-        'ADMIN_PASSWORD': '$PASSWORD',
+-        'STRICT_CHECKING': '0',
+-        'FAST_SUPPORT': have_fast_support,
+-        'TKT_SIG_SUPPORT': tkt_sig_support,
+-        'EXPECT_PAC': expect_pac,
+-        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-    })
++    environ=krb5_environ)
+ planpythontestsuite(
+     "ad_dc",
+     "samba.tests.krb5.fast_tests",
+-    environ={
+-        'ADMIN_USERNAME': '$USERNAME',
+-        'ADMIN_PASSWORD': '$PASSWORD',
+-        'STRICT_CHECKING': '0',
+-        'FAST_SUPPORT': have_fast_support,
+-        'TKT_SIG_SUPPORT': tkt_sig_support,
+-        'EXPECT_PAC': expect_pac,
+-        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-    })
++    environ=krb5_environ)
+ planpythontestsuite(
+     "ad_dc",
+     "samba.tests.krb5.ms_kile_client_principal_lookup_tests",
+-    environ={
+-        'ADMIN_USERNAME': '$USERNAME',
+-        'ADMIN_PASSWORD': '$PASSWORD',
+-        'STRICT_CHECKING': '0',
+-        'FAST_SUPPORT': have_fast_support,
+-        'TKT_SIG_SUPPORT': tkt_sig_support,
+-        'EXPECT_PAC': expect_pac,
+-        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-    })
++    environ=krb5_environ)
+ planpythontestsuite(
+     "ad_dc",
+     "samba.tests.krb5.spn_tests",
+-    environ={
+-        'ADMIN_USERNAME': '$USERNAME',
+-        'ADMIN_PASSWORD': '$PASSWORD',
+-        'STRICT_CHECKING': '0',
+-        'FAST_SUPPORT': have_fast_support,
+-        'TKT_SIG_SUPPORT': tkt_sig_support,
+-        'EXPECT_PAC': expect_pac,
+-        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-    })
++    environ=krb5_environ)
+ planpythontestsuite(
+     "ad_dc",
+     "samba.tests.krb5.alias_tests",
+-    environ={
+-        'ADMIN_USERNAME': '$USERNAME',
+-        'ADMIN_PASSWORD': '$PASSWORD',
+-        'STRICT_CHECKING': '0',
+-        'FAST_SUPPORT': have_fast_support,
+-        'TKT_SIG_SUPPORT': tkt_sig_support,
+-        'EXPECT_PAC': expect_pac,
+-        'EXPECT_EXTRA_PAC_BUFFERS': extra_pac_buffers
+-    })
++    environ=krb5_environ)
+ 
+ for env in [
+         'vampire_dc',
+-- 
+2.35.0
+
+
+From 111508005118e9f59614fb9e355a2b3f7a34f3cf Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 15 Jun 2022 19:37:39 +1200
+Subject: [PATCH 37/79] 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 at catalyst.net.nz>
+
+[jsutton at samba.org Brought in changes to add ATTRIBUTES_INFO and
+ REQUESTER_SID buffers to new PACs, and updated knownfails]
+
+[jsutton at 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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 4d685af7140..108c6055d0c 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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 a/source4/kdc/mit-kdb/kdb_samba_policies.c b/source4/kdc/mit-kdb/kdb_samba_policies.c
+index 7bc9a7b3347..3b25fff410b 100644
+--- a/source4/kdc/mit-kdb/kdb_samba_policies.c
++++ b/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 a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c
+index c2a604045d9..df2ba0a906f 100644
+--- a/source4/kdc/mit_samba.c
++++ b/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 a/source4/kdc/mit_samba.h b/source4/kdc/mit_samba.h
+index 636c77ec97c..4431e82a1b2 100644
+--- a/source4/kdc/mit_samba.h
++++ b/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.35.0
+
+
+From ea0ec2cbe48e2b08e2f51116e68c4e27f04544c0 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 16 Jun 2022 10:33:29 +1200
+Subject: [PATCH 38/79] heimdal:kdc: Accommodate NULL data parameter in
+ krb5_pac_get_buffer()
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+---
+ source4/heimdal/lib/krb5/pac.c | 10 ++++++----
+ 1 file changed, 6 insertions(+), 4 deletions(-)
+
+diff --git a/source4/heimdal/lib/krb5/pac.c b/source4/heimdal/lib/krb5/pac.c
+index 05bcc523080..100de904662 100644
+--- a/source4/heimdal/lib/krb5/pac.c
++++ b/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.35.0
+
+
+From 28eacb3e1c3b9155efa011a015f6ae1a07e346d2 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Fri, 27 May 2022 19:17:02 +1200
+Subject: [PATCH 39/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ source4/kdc/kpasswd-service-mit.c | 22 ++++++++++++----------
+ 1 file changed, 12 insertions(+), 10 deletions(-)
+
+diff --git a/source4/kdc/kpasswd-service-mit.c b/source4/kdc/kpasswd-service-mit.c
+index 9a014c058fe..aaed8b376ed 100644
+--- a/source4/kdc/kpasswd-service-mit.c
++++ b/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.35.0
+
+
+From 7e3b9542975d8379d87be8ae4a2c519d954f25d8 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Mon, 30 May 2022 19:17:41 +1200
+Subject: [PATCH 40/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ source4/kdc/kpasswd-service-mit.c | 94 ++++++++++++++++++++++++++-----
+ 1 file changed, 79 insertions(+), 15 deletions(-)
+
+diff --git a/source4/kdc/kpasswd-service-mit.c b/source4/kdc/kpasswd-service-mit.c
+index aaed8b376ed..293688efe19 100644
+--- a/source4/kdc/kpasswd-service-mit.c
++++ b/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.35.0
+
+
+From 5cfe32afa7bd5a9bb59d3c06aa2ee9a8f2ffe7ba Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 26 May 2022 16:34:01 +1200
+Subject: [PATCH 41/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ python/samba/tests/krb5/raw_testcase.py | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py
+index a2241707d44..4120edf93b9 100644
+--- a/python/samba/tests/krb5/raw_testcase.py
++++ b/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.35.0
+
+
+From e601bfc67e174ac54214013f3e86db0684c8d2cd Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 26 May 2022 20:52:04 +1200
+Subject: [PATCH 42/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at 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 a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py
+index 58b87eab25b..2117663b26b 100644
+--- a/python/samba/tests/krb5/kdc_base_test.py
++++ b/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 a/python/samba/tests/krb5/kdc_tgs_tests.py b/python/samba/tests/krb5/kdc_tgs_tests.py
+index 8cd27dec2aa..e52f46152fa 100755
+--- a/python/samba/tests/krb5/kdc_tgs_tests.py
++++ b/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.35.0
+
+
+From 6e648ed7121e3dc693994949e971a7ef801043c5 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 24 May 2022 19:06:53 +1200
+Subject: [PATCH 43/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at 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 a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py
+index 2117663b26b..685a6f71f88 100644
+--- a/python/samba/tests/krb5/kdc_base_test.py
++++ b/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 a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py
+index 4120edf93b9..a9a98c36cbf 100644
+--- a/python/samba/tests/krb5/raw_testcase.py
++++ b/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.35.0
+
+
+From 02d8030d62d7091291e81c6b7b98f45e718496d8 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 24 May 2022 19:13:54 +1200
+Subject: [PATCH 44/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at 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 a/python/samba/tests/krb5/rfc4120.asn1 b/python/samba/tests/krb5/rfc4120.asn1
+index e0831e1f86f..cac884be985 100644
+--- a/python/samba/tests/krb5/rfc4120.asn1
++++ b/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 a/python/samba/tests/krb5/rfc4120_constants.py b/python/samba/tests/krb5/rfc4120_constants.py
+index a9fdc5735dd..7f0f44500c7 100644
+--- a/python/samba/tests/krb5/rfc4120_constants.py
++++ b/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 a/python/samba/tests/krb5/rfc4120_pyasn1.py b/python/samba/tests/krb5/rfc4120_pyasn1.py
+index 348dd8c63fb..3c02b0efbc1 100644
+--- a/python/samba/tests/krb5/rfc4120_pyasn1.py
++++ b/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.35.0
+
+
+From f78dd8747c0c599f3ec71527b676478c809ef61e Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 24 May 2022 19:17:45 +1200
+Subject: [PATCH 45/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ python/samba/tests/krb5/raw_testcase.py | 95 +++++++++++++++++++++++++
+ 1 file changed, 95 insertions(+)
+
+diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py
+index a9a98c36cbf..df41dff688d 100644
+--- a/python/samba/tests/krb5/raw_testcase.py
++++ b/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.35.0
+
+
+From 9a8fc29789939e849d600f3c24bb9ad2b8511811 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 24 May 2022 19:21:37 +1200
+Subject: [PATCH 46/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ python/samba/tests/krb5/raw_testcase.py | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py
+index df41dff688d..421143781ae 100644
+--- a/python/samba/tests/krb5/raw_testcase.py
++++ b/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.35.0
+
+
+From 085043054e237732e6129bbafeb021f8ee5e2e04 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 24 May 2022 19:20:28 +1200
+Subject: [PATCH 47/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ python/samba/tests/krb5/raw_testcase.py | 44 +++++++++++++++----------
+ 1 file changed, 27 insertions(+), 17 deletions(-)
+
+diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py
+index 421143781ae..2aed5530455 100644
+--- a/python/samba/tests/krb5/raw_testcase.py
++++ b/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.35.0
+
+
+From f99d2c5b35f82752451f3d658b0ba3867b6ade13 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 24 May 2022 19:26:56 +1200
+Subject: [PATCH 48/79] tests/krb5: Fix enum typo
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ python/samba/tests/krb5/kdc_base_test.py | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py
+index 685a6f71f88..14f1d1a243d 100644
+--- a/python/samba/tests/krb5/kdc_base_test.py
++++ b/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.35.0
+
+
+From bdb78a93126a896fa63ddf281fb80e6813708e7c Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 24 May 2022 19:30:12 +1200
+Subject: [PATCH 49/79] tests/krb5: Add option for creating accounts with
+ expired passwords
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ python/samba/tests/krb5/kdc_base_test.py | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py
+index 14f1d1a243d..777b3b4aaf1 100644
+--- a/python/samba/tests/krb5/kdc_base_test.py
++++ b/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.35.0
+
+
+From 19e2333208eb1f053186ae68b1191961ca191958 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 24 May 2022 19:34:59 +1200
+Subject: [PATCH 50/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at samba.org Fixed conflict due to lacking rc4_support parameter]
+
+[jsutton at 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 a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py
+index 777b3b4aaf1..c0ca881985a 100644
+--- a/python/samba/tests/krb5/kdc_base_test.py
++++ b/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.35.0
+
+
+From 74f7862c320a574ba1a55402a0a6ff6c4e93c639 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 24 May 2022 19:57:57 +1200
+Subject: [PATCH 51/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at samba.org Fixed conflicts in imports]
+---
+ python/samba/tests/krb5/raw_testcase.py | 264 ++++++++++++++++++++++--
+ 1 file changed, 251 insertions(+), 13 deletions(-)
+
+diff --git a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py
+index 2aed5530455..57010ae73bd 100644
+--- a/python/samba/tests/krb5/raw_testcase.py
++++ b/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_checksum = self.Checksum_create(tgt.session_key,
+-                                                     KU_TGS_REQ_AUTH_CKSUM,
+-                                                     req_body_blob,
+-                                                     ctype=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)
+ 
+         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.35.0
+
+
+From 5882d1ece94890f898584c29ee8d458521a7b5b3 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 26 May 2022 16:35:03 +1200
+Subject: [PATCH 52/79] CVE-2022-32744 selftest: Specify Administrator kvno for
+ Python krb5 tests
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15074
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ source4/selftest/tests.py | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
+index 0d560dc23a3..4c68b85edf6 100755
+--- a/source4/selftest/tests.py
++++ b/source4/selftest/tests.py
+@@ -797,6 +797,7 @@ krb5_environ = {
+     'SERVICE_USERNAME': '$SERVER',
+     'ADMIN_USERNAME': '$DC_USERNAME',
+     'ADMIN_PASSWORD': '$DC_PASSWORD',
++    'ADMIN_KVNO': '1',
+     'FOR_USER': '$DC_USERNAME',
+     'STRICT_CHECKING':'0',
+     'FAST_SUPPORT': have_fast_support,
+-- 
+2.35.0
+
+
+From c59cc53e791c367cf1fa24f121d3a74723875615 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 24 May 2022 19:59:16 +1200
+Subject: [PATCH 53/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at samba.org Fixed conflicts in usage.py and knownfails; removed
+ MIT KDC 1.20-specific knownfails as it's not supported]
+
+[jsutton at 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 a/python/samba/tests/krb5/kdc_base_test.py b/python/samba/tests/krb5/kdc_base_test.py
+index c0ca881985a..f0306dde110 100644
+--- a/python/samba/tests/krb5/kdc_base_test.py
++++ b/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 a/python/samba/tests/krb5/kpasswd_tests.py b/python/samba/tests/krb5/kpasswd_tests.py
+new file mode 100755
+index 00000000000..3a6c7d818dc
+--- /dev/null
++++ b/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 a/python/samba/tests/krb5/raw_testcase.py b/python/samba/tests/krb5/raw_testcase.py
+index 57010ae73bd..4a78a8eadf3 100644
+--- a/python/samba/tests/krb5/raw_testcase.py
++++ b/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 a/python/samba/tests/usage.py b/python/samba/tests/usage.py
+index 881383d6e39..267deb14096 100644
+--- a/python/samba/tests/usage.py
++++ b/python/samba/tests/usage.py
+@@ -108,6 +108,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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 424a8b81c38..54e69a48bc1 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 108c6055d0c..53638afc17a 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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
+diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
+index 4c68b85edf6..9683efa4410 100755
+--- a/source4/selftest/tests.py
++++ b/source4/selftest/tests.py
+@@ -1448,6 +1448,10 @@ planpythontestsuite(
+     "ad_dc",
+     "samba.tests.krb5.alias_tests",
+     environ=krb5_environ)
++planoldpythontestsuite(
++    'ad_dc',
++    'samba.tests.krb5.kpasswd_tests',
++    environ=krb5_environ)
+ 
+ for env in [
+         'vampire_dc',
+-- 
+2.35.0
+
+
+From 241b7a4330f7d61a1e2bad617cb7d4926969dac3 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Fri, 27 May 2022 19:21:06 +1200
+Subject: [PATCH 54/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 54e69a48bc1..40e24f3155b 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 53638afc17a..a914c4d3492 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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 a/source4/kdc/kpasswd-helper.c b/source4/kdc/kpasswd-helper.c
+index 995f54825b5..55a2f5b3bf6 100644
+--- a/source4/kdc/kpasswd-helper.c
++++ b/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.35.0
+
+
+From 9ef77a0f69acbbc036b758d391e0ae599ea18a68 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 18 May 2022 16:48:59 +1200
+Subject: [PATCH 55/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at 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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index a914c4d3492..f64291e776d 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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 a/source4/kdc/kpasswd-service.c b/source4/kdc/kpasswd-service.c
+index b36cf402228..d3f700eca36 100644
+--- a/source4/kdc/kpasswd-service.c
++++ b/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.35.0
+
+
+From 35a8ceb68d9bf259cedc70274156a75215179f9f Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Fri, 27 May 2022 19:29:34 +1200
+Subject: [PATCH 56/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ lib/krb5_wrap/krb5_samba.c | 2 +-
+ selftest/knownfail_mit_kdc | 4 ++++
+ 2 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/lib/krb5_wrap/krb5_samba.c b/lib/krb5_wrap/krb5_samba.c
+index fff5b4e2a22..3d395db9a0f 100644
+--- a/lib/krb5_wrap/krb5_samba.c
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index f64291e776d..633bf79e8e0 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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.35.0
+
+
+From 2f18438d271573ab40207c14612701a42bd97bd1 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 18 May 2022 16:49:43 +1200
+Subject: [PATCH 57/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 40e24f3155b..3b494baa658 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 633bf79e8e0..f64291e776d 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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 a/source4/kdc/kpasswd-service.c b/source4/kdc/kpasswd-service.c
+index d3f700eca36..b76c904b697 100644
+--- a/source4/kdc/kpasswd-service.c
++++ b/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.35.0
+
+
+From 2b501504cb5e8640fbd2be663ed88435eb640205 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 18 May 2022 16:06:31 +1200
+Subject: [PATCH 58/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at 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 a/source4/auth/gensec/gensec_krb5.c b/source4/auth/gensec/gensec_krb5.c
+index 7d87b3ac6b9..104e4639c44 100644
+--- a/source4/auth/gensec/gensec_krb5.c
++++ b/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 a/source4/auth/gensec/gensec_krb5_helpers.c b/source4/auth/gensec/gensec_krb5_helpers.c
+new file mode 100644
+index 00000000000..21f2f1e884e
+--- /dev/null
++++ b/source4/auth/gensec/gensec_krb5_helpers.c
+@@ -0,0 +1,72 @@
++/*
++   Unix SMB/CIFS implementation.
++
++   Kerberos backend for GENSEC
++
++   Copyright (C) Andrew Bartlett <abartlet at 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 a/source4/auth/gensec/gensec_krb5_helpers.h b/source4/auth/gensec/gensec_krb5_helpers.h
+new file mode 100644
+index 00000000000..d7b694dad0c
+--- /dev/null
++++ b/source4/auth/gensec/gensec_krb5_helpers.h
+@@ -0,0 +1,32 @@
++/*
++   Unix SMB/CIFS implementation.
++
++   Kerberos backend for GENSEC
++
++   Copyright (C) Andrew Bartlett <abartlet at 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 a/source4/auth/gensec/gensec_krb5_internal.h b/source4/auth/gensec/gensec_krb5_internal.h
+new file mode 100644
+index 00000000000..0bb796f1b2a
+--- /dev/null
++++ b/source4/auth/gensec/gensec_krb5_internal.h
+@@ -0,0 +1,47 @@
++/*
++   Unix SMB/CIFS implementation.
++
++   Kerberos backend for GENSEC
++
++   Copyright (C) Andrew Bartlett <abartlet at 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 a/source4/auth/gensec/wscript_build b/source4/auth/gensec/wscript_build
+index d14a50ff273..20271f1665b 100644
+--- a/source4/auth/gensec/wscript_build
++++ b/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.35.0
+
+
+From ece3582acb8b99831678de8ed1b79942a35330bc Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 18 May 2022 16:52:41 +1200
+Subject: [PATCH 59/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 3b494baa658..5cd8615f6a9 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index f64291e776d..46b0f1fa9ed 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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 a/source4/kdc/kpasswd-service-heimdal.c b/source4/kdc/kpasswd-service-heimdal.c
+index 12c7402bf3f..40ce0ec90fa 100644
+--- a/source4/kdc/kpasswd-service-heimdal.c
++++ b/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 a/source4/kdc/kpasswd-service-mit.c b/source4/kdc/kpasswd-service-mit.c
+index 293688efe19..9513f2e57d6 100644
+--- a/source4/kdc/kpasswd-service-mit.c
++++ b/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 a/source4/kdc/wscript_build b/source4/kdc/wscript_build
+index 0edca94e75f..13ba3947cf6 100644
+--- a/source4/kdc/wscript_build
++++ b/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.35.0
+
+
+From 0245bfc57c34b882894731e9c223c10066e6980e Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 18 May 2022 17:11:49 +1200
+Subject: [PATCH 60/79] s4:kpasswd: Restructure code for clarity
+
+View with 'git show -b'.
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ source4/kdc/kpasswd-service-heimdal.c | 46 +++++++++++++--------------
+ 1 file changed, 22 insertions(+), 24 deletions(-)
+
+diff --git a/source4/kdc/kpasswd-service-heimdal.c b/source4/kdc/kpasswd-service-heimdal.c
+index 40ce0ec90fa..8e7847637aa 100644
+--- a/source4/kdc/kpasswd-service-heimdal.c
++++ b/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.35.0
+
+
+From 6708ab8eacac9ae0be5668decdda2f73eb64c9cd Mon Sep 17 00:00:00 2001
+From: Andreas Schneider <asn at samba.org>
+Date: Tue, 24 May 2022 10:17:00 +0200
+Subject: [PATCH 61/79] 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 at samba.org>
+Reviewed-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+
+[jsutton at 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 a/testprogs/blackbox/test_kpasswd_heimdal.sh b/testprogs/blackbox/test_kpasswd_heimdal.sh
+index 7351ce022d1..1e895daa162 100755
+--- a/testprogs/blackbox/test_kpasswd_heimdal.sh
++++ b/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.35.0
+
+
+From f6ec05c9f5b892b69a99532427acfad9454622e7 Mon Sep 17 00:00:00 2001
+From: Andreas Schneider <asn at samba.org>
+Date: Thu, 19 May 2022 16:35:28 +0200
+Subject: [PATCH 62/79] 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 at samba.org>
+Reviewed-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+---
+ testprogs/blackbox/test_kpasswd_heimdal.sh | 35 +++++++++++++++++++++-
+ 1 files changed, 34 insertions(+), 1 deletion(-)
+ create mode 100644 selftest/knownfail.d/kadmin_changepw
+
+diff --git a/testprogs/blackbox/test_kpasswd_heimdal.sh b/testprogs/blackbox/test_kpasswd_heimdal.sh
+index 1e895daa162..059b7a8e4d1 100755
+--- a/testprogs/blackbox/test_kpasswd_heimdal.sh
++++ b/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 at 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 at 03force%"
++
+ ###########################################################
+ ### Force password change at login
+ ###########################################################
+-- 
+2.35.0
+
+
+From a54818b9df81e30bf856f5fd343f6815e8233315 Mon Sep 17 00:00:00 2001
+From: Andreas Schneider <asn at samba.org>
+Date: Tue, 24 May 2022 09:54:18 +0200
+Subject: [PATCH 63/79] 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 at samba.org>
+Reviewed-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+
+[jsutton at samba.org Adapted entry to entry_ex->entry]
+---
+ source4/kdc/db-glue.c | 16 +++++++++++-----
+ 1 file changed, 11 insertions(+), 5 deletions(-)
+
+diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
+index c4ca2c3fcba..c35b94c8553 100644
+--- a/source4/kdc/db-glue.c
++++ b/source4/kdc/db-glue.c
+@@ -779,6 +779,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.
+  */
+@@ -1073,11 +1081,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.35.0
+
+
+From 2fe609dc0cb14208a02003e861a76373b5eecc9f Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 18 May 2022 16:56:01 +1200
+Subject: [PATCH 64/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at samba.org Adapted entry to entry_ex->entry]
+
+[jsutton at samba.org Fixed conflicts caused by superfluous whitespace]
+---
+ source4/kdc/db-glue.c | 192 +++++++++++++++++++++++-------------------
+ 1 file changed, 107 insertions(+), 85 deletions(-)
+
+diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
+index c35b94c8553..68c4c49f22d 100644
+--- a/source4/kdc/db-glue.c
++++ b/source4/kdc/db-glue.c
+@@ -787,6 +787,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.
+  */
+@@ -876,93 +971,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 */
+@@ -1148,6 +1158,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.35.0
+
+
+From 23b7197193ed7af9897acd868d1213274b084a55 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 25 May 2022 17:19:58 +1200
+Subject: [PATCH 65/79] 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 at catalyst.net.nz>
+Pair-Programmed-With: Andreas Schneider <asn at samba.org>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ source4/kdc/db-glue.c | 116 ++++++++++++++++++++----------------------
+ 1 file changed, 55 insertions(+), 61 deletions(-)
+
+diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
+index 68c4c49f22d..8d8099cddd4 100644
+--- a/source4/kdc/db-glue.c
++++ b/source4/kdc/db-glue.c
+@@ -797,7 +797,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
+@@ -811,75 +812,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 */
+-
+-		/* this has to be with malloc() */
+-		ret = smb_krb5_principal_set_realm(context, *out_princ, lpcfg_realm(lp_ctx));
+-		if (ret) {
+-			return ret;
+-		}
++		code = smb_krb5_make_principal(context,
++					      out_princ,
++					      lpcfg_realm(lp_ctx),
++					      samAccountName,
++					      NULL);
++		return code;
+ 	}
+ 
+-	return 0;
++	/*
++	 * 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;
++	}
++
++	/*
++	 * 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.35.0
+
+
+From 380bbb969f83e8c846eb4e6c20c53a2d1f0b6395 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 18 May 2022 16:56:01 +1200
+Subject: [PATCH 66/79] 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 at REALM would result in a ticket to krbtgt at 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 at samba.org>
+Signed-off-by: Andreas Schneider <asn at samba.org>
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at samba.org Adapted entry to entry_ex->entry; removed MIT KDC
+ 1.20-specific knownfails]
+---
+ selftest/knownfail_heimdal_kdc       |  2 -
+ source4/kdc/db-glue.c                | 84 +++++++++++++++-------------
+ 2 files changed, 46 insertions(+), 39 deletions(-)
+ delete mode 100644 selftest/knownfail.d/kadmin_changepw
+
+diff --git a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 5cd8615f6a9..49ab29f115d 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
+index 8d8099cddd4..7b552f8756c 100644
+--- a/source4/kdc/db-glue.c
++++ b/source4/kdc/db-glue.c
+@@ -793,6 +793,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)
+ {
+@@ -812,46 +813,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) {
++	/*
++	 * 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;
++		}
++
++		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;
+ 		}
+-		smb_krb5_principal_set_type(context,
+-					    *out_princ,
+-					    KRB5_NT_SRV_INST);
+-
+-		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;
+ 	}
+ 
+ 	/*
+@@ -1157,6 +1164,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.35.0
+
+
+From 9447ba23ffb70f08d87700c94c6a48ad20003f5a Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 24 May 2022 17:53:49 +1200
+Subject: [PATCH 67/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at samba.org Adapted entry to entry_ex->entry; included
+ samba_kdc.h header file]
+
+[jsutton at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 49ab29f115d..387ccea3ba7 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 46b0f1fa9ed..c2a31b4a140 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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 a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
+index 7b552f8756c..3241cb1cfb2 100644
+--- a/source4/kdc/db-glue.c
++++ b/source4/kdc/db-glue.c
+@@ -1189,6 +1189,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 a/source4/kdc/mit-kdb/kdb_samba_principals.c b/source4/kdc/mit-kdb/kdb_samba_principals.c
+index cc67c2392be..2059ffa855e 100644
+--- a/source4/kdc/mit-kdb/kdb_samba_principals.c
++++ b/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 a/source4/kdc/samba_kdc.h b/source4/kdc/samba_kdc.h
+index e228a82ce6a..8010d7c35ed 100644
+--- a/source4/kdc/samba_kdc.h
++++ b/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.35.0
+
+
+From c433b6059f82aaa5b07359b112c684e0e0eab061 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Mon, 30 May 2022 19:18:17 +1200
+Subject: [PATCH 68/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 387ccea3ba7..afb9bcf1209 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c
+index 38dba8493ae..15be136496f 100644
+--- a/source4/heimdal/kdc/krb5tgs.c
++++ b/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.35.0
+
+
+From 949a53e19d4b885de13d756aa9052c1066679447 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 14 Jun 2022 15:23:55 +1200
+Subject: [PATCH 69/79] CVE-2022-2031 tests/krb5: Test truncated forms of
+ server principals
+
+We should not be able to use krb at REALM instead of krbtgt at REALM.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at 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 a/python/samba/tests/krb5/as_req_tests.py b/python/samba/tests/krb5/as_req_tests.py
+index 315720f85d6..054a49b64aa 100755
+--- a/python/samba/tests/krb5/as_req_tests.py
++++ b/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 at REALM
++    # instead of krbtgt at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index afb9bcf1209..dbfff5784e6 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index c2a31b4a140..0f90ea10299 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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.35.0
+
+
+From 08835c6f2078f999f2e2dfd218a988f274342acc Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 25 May 2022 20:00:55 +1200
+Subject: [PATCH 70/79] 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 at REALM' would erroneously be
+considered equal to 'krbtgt at REALM'.
+
+BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index dbfff5784e6..afb9bcf1209 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 0f90ea10299..c2a31b4a140 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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 a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
+index 3241cb1cfb2..9a4dbb10414 100644
+--- a/source4/kdc/db-glue.c
++++ b/source4/kdc/db-glue.c
+@@ -732,15 +732,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;
+@@ -752,13 +756,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.35.0
+
+
+From 4726679c1062bf6c108bd578f2b4e200a8058798 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 26 May 2022 16:36:30 +1200
+Subject: [PATCH 71/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at 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 a/source4/kdc/kdc-heimdal.c b/source4/kdc/kdc-heimdal.c
+index 28dadcb1fd5..b807c66e5d0 100644
+--- a/source4/kdc/kdc-heimdal.c
++++ b/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 a/source4/kdc/kdc-server.h b/source4/kdc/kdc-server.h
+index fd883c2e4b4..89b30f122f5 100644
+--- a/source4/kdc/kdc-server.h
++++ b/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 a/source4/kdc/kdc-service-mit.c b/source4/kdc/kdc-service-mit.c
+index dd6902f083d..1ea2d614064 100644
+--- a/source4/kdc/kdc-service-mit.c
++++ b/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 a/source4/kdc/kpasswd-service.c b/source4/kdc/kpasswd-service.c
+index b76c904b697..e7e9b3766a3 100644
+--- a/source4/kdc/kpasswd-service.c
++++ b/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.35.0
+
+
+From fbfa36edd8030260a8a89c8bf3dc1befc5c2a4d8 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 8 Jun 2022 13:53:29 +1200
+Subject: [PATCH 72/79] s4:kdc: Remove kadmin mode from HDB plugin
+
+It appears we no longer require it.
+
+Signed-off-by: Joseph Sutton <josephsutton at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ source4/kdc/hdb-samba4-plugin.c | 33 ++++++---------------------------
+ 1 file changed, 6 insertions(+), 27 deletions(-)
+
+diff --git a/source4/kdc/hdb-samba4-plugin.c b/source4/kdc/hdb-samba4-plugin.c
+index 6f76124995d..4b90a766f76 100644
+--- a/source4/kdc/hdb-samba4-plugin.c
++++ b/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);
++	void *ptr = NULL;
++	struct samba_kdc_base_context *base_ctx = NULL;
+ 
+-		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 {
++	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.35.0
+
+
+From 68292f3fe03d2f7b563fa0aec50171f7ed4313ff Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 26 May 2022 16:39:20 +1200
+Subject: [PATCH 73/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at samba.org Fixed knownfail conflicts]
+
+[jsutton at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index afb9bcf1209..0d93253f999 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/source4/kdc/hdb-samba4-plugin.c b/source4/kdc/hdb-samba4-plugin.c
+index 4b90a766f76..dba25e825de 100644
+--- a/source4/kdc/hdb-samba4-plugin.c
++++ b/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 a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c
+index f0939193ad7..ff108c2c186 100644
+--- a/source4/kdc/hdb-samba4.c
++++ b/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);
+@@ -572,3 +621,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 a/source4/kdc/kdc-glue.h b/source4/kdc/kdc-glue.h
+index c083b8c6429..ff8684e1666 100644
+--- a/source4/kdc/kdc-glue.h
++++ b/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.35.0
+
+
+From 7421cc5874c86fb2ba3bf4cd534aef327f66d049 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Mon, 30 May 2022 19:16:02 +1200
+Subject: [PATCH 74/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 0d93253f999..424a8b81c38 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index c2a31b4a140..0d2f5bab6d2 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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 a/source4/kdc/kpasswd-service.c b/source4/kdc/kpasswd-service.c
+index e7e9b3766a3..a71a9b04ea7 100644
+--- a/source4/kdc/kpasswd-service.c
++++ b/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.35.0
+
+
+From 0605875c774b25eb0dfe47a38cef4ff11da3de26 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Fri, 10 Jun 2022 19:17:11 +1200
+Subject: [PATCH 75/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at samba.org Fixed knownfail conflicts]
+
+[jsutton at 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 a/python/samba/tests/krb5/kpasswd_tests.py b/python/samba/tests/krb5/kpasswd_tests.py
+index 3a6c7d818dc..0db857f7bbd 100755
+--- a/python/samba/tests/krb5/kpasswd_tests.py
++++ b/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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 424a8b81c38..42beccaed58 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 0d2f5bab6d2..9fc34e5d8db 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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.35.0
+
+
+From d8711586fec305827a3ac8f1b21120eac7bd2c90 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Fri, 10 Jun 2022 19:18:07 +1200
+Subject: [PATCH 76/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at 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 a/auth/auth_sam_reply.c b/auth/auth_sam_reply.c
+index b5b6362dc93..2e27e5715d1 100644
+--- a/auth/auth_sam_reply.c
++++ b/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 a/auth/auth_util.c b/auth/auth_util.c
+index fe01babd107..ec9094d0f15 100644
+--- a/auth/auth_util.c
++++ b/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 a/librpc/idl/auth.idl b/librpc/idl/auth.idl
+index 1092935b971..f7658cdde28 100644
+--- a/librpc/idl/auth.idl
++++ b/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 a/source4/auth/ntlm/auth_developer.c b/source4/auth/ntlm/auth_developer.c
+index 551f0ae1605..b49a6fbabea 100644
+--- a/source4/auth/ntlm/auth_developer.c
++++ b/source4/auth/ntlm/auth_developer.c
+@@ -74,7 +74,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 a/source4/auth/sam.c b/source4/auth/sam.c
+index 93b41be3b21..0818e792cae 100644
+--- a/source4/auth/sam.c
++++ b/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 a/source4/auth/session.c b/source4/auth/session.c
+index c5fc226a7d7..2396fca6843 100644
+--- a/source4/auth/session.c
++++ b/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 a/source4/auth/system_session.c b/source4/auth/system_session.c
+index e9cff3d55d6..713c5b66646 100644
+--- a/source4/auth/system_session.c
++++ b/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.35.0
+
+
+From 7d00300074ee2a7152b976762b28d01081800e90 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Fri, 10 Jun 2022 19:18:35 +1200
+Subject: [PATCH 77/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+---
+ source4/auth/kerberos/kerberos_pac.c | 44 ++++++++++++++++++++++++++++
+ 1 file changed, 44 insertions(+)
+
+diff --git a/source4/auth/kerberos/kerberos_pac.c b/source4/auth/kerberos/kerberos_pac.c
+index 54ef4d61b02..bd0ae20e007 100644
+--- a/source4/auth/kerberos/kerberos_pac.c
++++ b/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.35.0
+
+
+From ee520e1d3541fd514fc4c170b289cc3cdcbb9e1c Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Fri, 10 Jun 2022 19:18:53 +1200
+Subject: [PATCH 78/79] 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 at catalyst.net.nz>
+Reviewed-by: Andreas Schneider <asn at samba.org>
+
+[jsutton at samba.org Fixed knownfail conflicts]
+
+[jsutton at 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 a/selftest/knownfail_heimdal_kdc b/selftest/knownfail_heimdal_kdc
+index 42beccaed58..424a8b81c38 100644
+--- a/selftest/knownfail_heimdal_kdc
++++ b/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 a/selftest/knownfail_mit_kdc b/selftest/knownfail_mit_kdc
+index 9fc34e5d8db..0d2f5bab6d2 100644
+--- a/selftest/knownfail_mit_kdc
++++ b/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 a/source4/kdc/kpasswd-helper.c b/source4/kdc/kpasswd-helper.c
+index 55a2f5b3bf6..2ffdb79aea5 100644
+--- a/source4/kdc/kpasswd-helper.c
++++ b/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 a/source4/kdc/kpasswd-helper.h b/source4/kdc/kpasswd-helper.h
+index 8fad81e0a5d..94a6e2acfdd 100644
+--- a/source4/kdc/kpasswd-helper.h
++++ b/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 a/source4/kdc/kpasswd-service-heimdal.c b/source4/kdc/kpasswd-service-heimdal.c
+index 8e7847637aa..779a969566a 100644
+--- a/source4/kdc/kpasswd-service-heimdal.c
++++ b/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 a/source4/kdc/kpasswd-service-mit.c b/source4/kdc/kpasswd-service-mit.c
+index 9513f2e57d6..5111f47915d 100644
+--- a/source4/kdc/kpasswd-service-mit.c
++++ b/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.35.0
+
+
+From 87b0b1c570bc2fdb7b25c61d495ab206235eed9a Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 23 Jun 2022 13:59:11 +1200
+Subject: [PATCH 79/79] 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 at catalyst.net.nz>
+
+[jsutton at 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 a/testprogs/blackbox/test_kinit_trusts_heimdal.sh b/testprogs/blackbox/test_kinit_trusts_heimdal.sh
+index bf0b81a0473..621434eac35 100755
+--- a/testprogs/blackbox/test_kinit_trusts_heimdal.sh
++++ b/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.35.0
+
diff -Nru samba-4.13.13+dfsg/debian/patches/ldb-memory-bug-15096-4.13-v3.patch samba-4.13.13+dfsg/debian/patches/ldb-memory-bug-15096-4.13-v3.patch
--- samba-4.13.13+dfsg/debian/patches/ldb-memory-bug-15096-4.13-v3.patch	1970-01-01 03:00:00.000000000 +0300
+++ samba-4.13.13+dfsg/debian/patches/ldb-memory-bug-15096-4.13-v3.patch	2022-08-01 17:48:14.000000000 +0300
@@ -0,0 +1,2341 @@
+From e9b06385e8799fc2e414aa1535250714a37f09db Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 14 Jun 2022 21:09:53 +1200
+Subject: [PATCH 01/18] 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 at catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/objectclass_attrs.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c b/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c
+index 0b9725e2767..c90e547afe2 100644
+--- a/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c
++++ b/source4/dsdb/samdb/ldb_modules/objectclass_attrs.c
+@@ -261,7 +261,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.35.0
+
+
+From 6b94861159b6e5fc7b83063bfd413f144c438df5 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 21 Jun 2022 15:37:15 +1200
+Subject: [PATCH 02/18] 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 at catalyst.net.nz>
+---
+ source4/dsdb/tests/python/acl.py    | 26 ++++++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+diff --git a/source4/dsdb/tests/python/acl.py b/source4/dsdb/tests/python/acl.py
+index 53acb99c296..a3c42b00a08 100755
+--- a/source4/dsdb/tests/python/acl.py
++++ b/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.35.0
+
+
+From 28b87fdfefa965d955b0e9467b08692b972e40eb Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 21 Jun 2022 14:41:02 +1200
+Subject: [PATCH 03/18] 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 at catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/partition.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/partition.c b/source4/dsdb/samdb/ldb_modules/partition.c
+index 2544a106d13..2d90ca5d1b3 100644
+--- a/source4/dsdb/samdb/ldb_modules/partition.c
++++ b/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.35.0
+
+
+From 1fb5b08537f230e6ef02b7dfae1de802810c96b0 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 21 Jun 2022 14:49:51 +1200
+Subject: [PATCH 04/18] 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 at catalyst.net.nz>
+---
+ source4/torture/drs/rpc/dssync.c | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/source4/torture/drs/rpc/dssync.c b/source4/torture/drs/rpc/dssync.c
+index 6a8a8b5492b..53b9c000a06 100644
+--- a/source4/torture/drs/rpc/dssync.c
++++ b/source4/torture/drs/rpc/dssync.c
+@@ -525,7 +525,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.35.0
+
+
+From f5c0790ba29ce67c3633b8b6b3c645544d57ac73 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 21 Jun 2022 15:22:47 +1200
+Subject: [PATCH 05/18] 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 at catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/acl.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/acl.c b/source4/dsdb/samdb/ldb_modules/acl.c
+index 712724909e3..2fd01c587af 100644
+--- a/source4/dsdb/samdb/ldb_modules/acl.c
++++ b/source4/dsdb/samdb/ldb_modules/acl.c
+@@ -731,8 +731,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.35.0
+
+
+From 07b8a1d0899f016bb313eb1b961c0fe04330ab19 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 16 Feb 2022 12:43:52 +1300
+Subject: [PATCH 06/18] 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 at catalyst.net.nz>
+---
+ lib/ldb/modules/rdn_name.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lib/ldb/modules/rdn_name.c b/lib/ldb/modules/rdn_name.c
+index e69ad9315ae..25cffe07591 100644
+--- a/lib/ldb/modules/rdn_name.c
++++ b/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.35.0
+
+
+From 2a7b8e6b737c8b3842781807090fd4e7bb339200 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 14 Jun 2022 19:49:19 +1200
+Subject: [PATCH 07/18] 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 at catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/repl_meta_data.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+index 870185ee1d3..e7020f588e5 100644
+--- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
++++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+@@ -3523,7 +3523,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);
+ 		}
+ 
+@@ -3556,11 +3556,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);
+ 		}
+ 
+@@ -3643,7 +3643,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.35.0
+
+
+From 51d40e420f0d306fe4b3ea326b6a57988311e60d Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 14 Jun 2022 21:11:33 +1200
+Subject: [PATCH 08/18] 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 at catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c
+index 64e05195798..5f8911c66be 100644
+--- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c
++++ b/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.35.0
+
+
+From f59fb058ebb38ce91bf191b5cd888145b4776f42 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Tue, 14 Jun 2022 21:12:39 +1200
+Subject: [PATCH 09/18] 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 at catalyst.net.nz>
+---
+ source4/lib/registry/ldb.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/source4/lib/registry/ldb.c b/source4/lib/registry/ldb.c
+index c0b13e0d4ce..ab5a0f3a128 100644
+--- a/source4/lib/registry/ldb.c
++++ b/source4/lib/registry/ldb.c
+@@ -856,7 +856,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.35.0
+
+
+From 37a606628f14d585ea12e1dd4e680dda92ce4620 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Mon, 21 Feb 2022 16:10:32 +1300
+Subject: [PATCH 10/18] 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 at 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 a/lib/ldb/common/ldb_msg.c b/lib/ldb/common/ldb_msg.c
+index 57dfc5a04c2..2a9ce384bb9 100644
+--- a/lib/ldb/common/ldb_msg.c
++++ b/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 a/lib/ldb/include/ldb_module.h b/lib/ldb/include/ldb_module.h
+index 8c1e5ee7936..4c7c85a17f0 100644
+--- a/lib/ldb/include/ldb_module.h
++++ b/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.35.0
+
+
+From e71ad28101f0703a80873b11459d0c9cef8fad5a Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 16 Feb 2022 12:35:13 +1300
+Subject: [PATCH 11/18] 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 at 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 a/lib/ldb/common/ldb_msg.c b/lib/ldb/common/ldb_msg.c
+index 2a9ce384bb9..44d3b29e9a7 100644
+--- a/lib/ldb/common/ldb_msg.c
++++ b/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 a/lib/ldb/include/ldb.h b/lib/ldb/include/ldb.h
+index f5f02c9a344..45b91cd5810 100644
+--- a/lib/ldb/include/ldb.h
++++ b/lib/ldb/include/ldb.h
+@@ -1980,6 +1980,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 a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c
+index 62e04d08003..38d12c1c2b1 100644
+--- a/source4/dsdb/common/util.c
++++ b/source4/dsdb/common/util.c
+@@ -812,7 +812,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;
+@@ -847,14 +847,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;
+ }
+@@ -868,7 +864,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;
+@@ -903,14 +899,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.35.0
+
+
+From a5d537e7b8b0ce292dcfe2093a54becaba6c3464 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 16 Feb 2022 16:30:03 +1300
+Subject: [PATCH 12/18] 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 at 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 a/lib/ldb/common/ldb_msg.c b/lib/ldb/common/ldb_msg.c
+index 44d3b29e9a7..9cd7998e21c 100644
+--- a/lib/ldb/common/ldb_msg.c
++++ b/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 a/lib/ldb/include/ldb.h b/lib/ldb/include/ldb.h
+index 45b91cd5810..0c3dbc3f22d 100644
+--- a/lib/ldb/include/ldb.h
++++ b/lib/ldb/include/ldb.h
+@@ -2001,12 +2001,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.35.0
+
+
+From 884513d579ecb858e9fce5677d7314e82fcee4f2 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Mon, 21 Feb 2022 16:27:37 +1300
+Subject: [PATCH 13/18] 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 at 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 a/lib/ldb/ldb_map/ldb_map.c b/lib/ldb/ldb_map/ldb_map.c
+index b453dff80d2..c7b0c228631 100644
+--- a/lib/ldb/ldb_map/ldb_map.c
++++ b/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 a/lib/ldb/ldb_map/ldb_map_inbound.c b/lib/ldb/ldb_map/ldb_map_inbound.c
+index 324295737da..50b9427c26c 100644
+--- a/lib/ldb/ldb_map/ldb_map_inbound.c
++++ b/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 a/lib/ldb/modules/rdn_name.c b/lib/ldb/modules/rdn_name.c
+index 25cffe07591..3cb62bf567b 100644
+--- a/lib/ldb/modules/rdn_name.c
++++ b/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 a/source3/passdb/pdb_samba_dsdb.c b/source3/passdb/pdb_samba_dsdb.c
+index 276bda88efc..a9a04e1f097 100644
+--- a/source3/passdb/pdb_samba_dsdb.c
++++ b/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 a/source4/dns_server/dnsserver_common.c b/source4/dns_server/dnsserver_common.c
+index 8635d075be8..afc9387466c 100644
+--- a/source4/dns_server/dnsserver_common.c
++++ b/source4/dns_server/dnsserver_common.c
+@@ -1090,15 +1090,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 a/source4/dsdb/common/util.c b/source4/dsdb/common/util.c
+index 38d12c1c2b1..dc2edb6d730 100644
+--- a/source4/dsdb/common/util.c
++++ b/source4/dsdb/common/util.c
+@@ -920,6 +920,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
+  *
+@@ -938,6 +948,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
+ */
+@@ -969,6 +985,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
+ */
+@@ -2810,15 +2888,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);
+@@ -3183,6 +3254,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,
+@@ -5652,7 +5724,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
+@@ -5664,21 +5737,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;
+@@ -5688,7 +5759,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
+@@ -5700,17 +5772,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 a/source4/dsdb/samdb/ldb_modules/descriptor.c b/source4/dsdb/samdb/ldb_modules/descriptor.c
+index daa08c2ebc7..4b01961dcb0 100644
+--- a/source4/dsdb/samdb/ldb_modules/descriptor.c
++++ b/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 a/source4/dsdb/samdb/ldb_modules/objectguid.c b/source4/dsdb/samdb/ldb_modules/objectguid.c
+index bc3260cf0d8..0fe995a5763 100644
+--- a/source4/dsdb/samdb/ldb_modules/objectguid.c
++++ b/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 a/source4/dsdb/samdb/ldb_modules/partition_init.c b/source4/dsdb/samdb/ldb_modules/partition_init.c
+index 58c65ccedd0..484b5bffb27 100644
+--- a/source4/dsdb/samdb/ldb_modules/partition_init.c
++++ b/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 a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+index e7020f588e5..dcfbfdb7fc7 100644
+--- a/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
++++ b/source4/dsdb/samdb/ldb_modules/repl_meta_data.c
+@@ -3886,22 +3886,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));
+@@ -5159,16 +5149,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) {
++	if (ldb_msg_append_value(msg, rdn_name, rdn_val, LDB_FLAG_MOD_REPLACE) != 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) {
+-		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 a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c
+index a219446bba7..b9677442e3f 100644
+--- a/source4/dsdb/samdb/ldb_modules/samldb.c
++++ b/source4/dsdb/samdb/ldb_modules/samldb.c
+@@ -1100,14 +1100,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);
+ 	}
+@@ -1789,7 +1786,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;
+ 
+@@ -1923,23 +1920,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 */
+@@ -2015,14 +2006,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;
+ 	}
+@@ -2940,26 +2930,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;
+ 	}
+ 
+ 	/*
+@@ -2970,14 +2957,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") &&
+@@ -2990,14 +2975,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 */
+@@ -3200,13 +3183,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;
+ }
+@@ -3304,13 +3286,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 a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c b/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c
+index 5f8911c66be..99c5955e9e7 100644
+--- a/source4/dsdb/samdb/ldb_modules/tombstone_reanimate.c
++++ b/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 a/source4/nbt_server/wins/winsdb.c b/source4/nbt_server/wins/winsdb.c
+index be1d8ba8050..bb205d66111 100644
+--- a/source4/nbt_server/wins/winsdb.c
++++ b/source4/nbt_server/wins/winsdb.c
+@@ -99,13 +99,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);
+@@ -776,8 +774,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 a/source4/rpc_server/lsa/dcesrv_lsa.c b/source4/rpc_server/lsa/dcesrv_lsa.c
+index 8333cb149b6..930145099e0 100644
+--- a/source4/rpc_server/lsa/dcesrv_lsa.c
++++ b/source4/rpc_server/lsa/dcesrv_lsa.c
+@@ -1757,12 +1757,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;
+ 	}
+@@ -1853,13 +1848,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;
+ 		}
+@@ -2145,28 +2134,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;
+ 			}
+@@ -4614,14 +4605,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 a/source4/winbind/idmap.c b/source4/winbind/idmap.c
+index c4039be473a..c6375f8357a 100644
+--- a/source4/winbind/idmap.c
++++ b/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.35.0
+
+
+From 11a8dcfd4802a14e33d5d704dce1c1216029eebe Mon Sep 17 00:00:00 2001
+From: Andrew Bartlett <abartlet at samba.org>
+Date: Tue, 14 Jun 2022 15:43:26 +1200
+Subject: [PATCH 14/18] CVE-2022-32746 ldb: Release LDB 2.2.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 at samba.org>
+---
+ lib/ldb/ABI/ldb-2.2.4.sigs        | 291 ++++++++++++++++++++++++++++++
+ lib/ldb/ABI/pyldb-util-2.2.4.sigs |   3 +
+ lib/ldb/wscript                   |   2 +-
+ 3 files changed, 295 insertions(+), 1 deletion(-)
+ create mode 100644 lib/ldb/ABI/ldb-2.2.4.sigs
+ create mode 100644 lib/ldb/ABI/pyldb-util-2.2.4.sigs
+
+diff --git a/lib/ldb/ABI/ldb-2.2.4.sigs b/lib/ldb/ABI/ldb-2.2.4.sigs
+new file mode 100644
+index 00000000000..40388d9e330
+--- /dev/null
++++ b/lib/ldb/ABI/ldb-2.2.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 a/lib/ldb/ABI/pyldb-util-2.2.4.sigs b/lib/ldb/ABI/pyldb-util-2.2.4.sigs
+new file mode 100644
+index 00000000000..164a806b2ff
+--- /dev/null
++++ b/lib/ldb/ABI/pyldb-util-2.2.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 *)
+diff --git a/lib/ldb/wscript b/lib/ldb/wscript
+index 57dfdd6fe6b..216cd70f9fa 100644
+--- a/lib/ldb/wscript
++++ b/lib/ldb/wscript
+@@ -2,7 +2,7 @@
+ 
+ APPNAME = 'ldb'
+ # For Samba 4.13.x
+-VERSION = '2.2.3'
++VERSION = '2.2.4'
+ 
+ import sys, os
+ 
+-- 
+2.35.0
+
+
+From d72aeda6a2e7ba8374eee7d193b315bf3374641c Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Wed, 16 Feb 2022 17:03:10 +1300
+Subject: [PATCH 15/18] 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 at catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/samldb.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/samldb.c b/source4/dsdb/samdb/ldb_modules/samldb.c
+index b9677442e3f..c68ffa298c3 100644
+--- a/source4/dsdb/samdb/ldb_modules/samldb.c
++++ b/source4/dsdb/samdb/ldb_modules/samldb.c
+@@ -748,7 +748,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;
+ 	}
+ 
+@@ -916,7 +916,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.35.0
+
+
+From c402505add4c8f249abc6f5031e7cfada8882d78 Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 17 Feb 2022 11:11:53 +1300
+Subject: [PATCH 16/18] 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 at catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/util.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c
+index 4c67873643a..5d418efcd52 100644
+--- a/source4/dsdb/samdb/ldb_modules/util.c
++++ b/source4/dsdb/samdb/ldb_modules/util.c
+@@ -1544,7 +1544,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.35.0
+
+
+From 46da0b33a77a99ead5bdda0c375c665dd724c94b Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Thu, 17 Feb 2022 11:13:38 +1300
+Subject: [PATCH 17/18] 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 at catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/util.c | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c
+index 5d418efcd52..af412f55f98 100644
+--- a/source4/dsdb/samdb/ldb_modules/util.c
++++ b/source4/dsdb/samdb/ldb_modules/util.c
+@@ -1546,15 +1546,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.35.0
+
+
+From 38afcdacd8b43cee6860b58beece8f7ab4c5441c Mon Sep 17 00:00:00 2001
+From: Joseph Sutton <josephsutton at catalyst.net.nz>
+Date: Fri, 3 Jun 2022 16:16:31 +1200
+Subject: [PATCH 18/18] 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 at catalyst.net.nz>
+---
+ source4/dsdb/samdb/ldb_modules/util.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/source4/dsdb/samdb/ldb_modules/util.c b/source4/dsdb/samdb/ldb_modules/util.c
+index af412f55f98..5ccbb1b4360 100644
+--- a/source4/dsdb/samdb/ldb_modules/util.c
++++ b/source4/dsdb/samdb/ldb_modules/util.c
+@@ -1557,7 +1557,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.35.0
+
diff -Nru samba-4.13.13+dfsg/debian/patches/series samba-4.13.13+dfsg/debian/patches/series
--- samba-4.13.13+dfsg/debian/patches/series	2022-05-28 22:52:17.000000000 +0300
+++ samba-4.13.13+dfsg/debian/patches/series	2022-08-01 17:48:14.000000000 +0300
@@ -299,3 +299,6 @@
 bug1005642-s3-includes-Make-the-comments-describing-itime-consi.patch
 bug998423-s3-mdssvc-Correctly-disconnect-the-VFS-connection-in.patch
 bug998423-s3-smbd-In-create_conn_struct_cwd-don-t-TALLOC_FREE-.patch
+CVE-2022-32742-bug-15085-4.13.patch
+ldb-memory-bug-15096-4.13-v3.patch
+kpasswd_bugs_v15_4-13.patch


More information about the Pkg-samba-maint mailing list