From bed2ea1d378f31e3d071a7a5d4c80cd9cc1c9894 Mon Sep 17 00:00:00 2001
From: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Date: Wed, 20 Oct 2021 17:20:54 +1300
Subject: [PATCH 172/266] CVE-2020-25722 s4/dsdb/pwd_hash: rework pwdLastSet
 bypass

This tightens the logic a bit, in that a message with trailing DELETE
elements is no longer accepted when the bypass flag is set. In any case
this is an unlikely scenario as this is an internal flag set by a private
control in pdb_samba_dsdb_replace_by_sam().

BUG: https://bugzilla.samba.org/show_bug.cgi?id=14876

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>

Conflict:NA
Reference:https://gitlab.com/samba-team/samba/-/commit/bed2ea1d378f31e3d071a7a5d4c80cd9cc1c9894

---
 .../dsdb/samdb/ldb_modules/password_hash.c    | 28 ++++++++++++-------
 1 file changed, 18 insertions(+), 10 deletions(-)

diff --git a/source4/dsdb/samdb/ldb_modules/password_hash.c b/source4/dsdb/samdb/ldb_modules/password_hash.c
index 5f033f9622b..9fa2e36ba90 100644
--- a/source4/dsdb/samdb/ldb_modules/password_hash.c
+++ b/source4/dsdb/samdb/ldb_modules/password_hash.c
@@ -2227,23 +2227,31 @@ static int setup_last_set_field(struct setup_password_fields_io *io)
 	}
 
 	if (io->ac->pwd_last_set_bypass) {
-		struct ldb_message_element *el1 = NULL;
-		struct ldb_message_element *el2 = NULL;
-
+		struct ldb_message_element *el = NULL;
+		size_t i;
+		size_t count = 0;
+		/*
+		 * This is a message from pdb_samba_dsdb_replace_by_sam()
+		 *
+		 * We want to ensure there is only one pwdLastSet element, and
+		 * it isn't deleting.
+		 */
 		if (msg == NULL) {
 			return LDB_ERR_CONSTRAINT_VIOLATION;
 		}
 
-		el1 = dsdb_get_single_valued_attr(msg, "pwdLastSet",
-						  io->ac->req->operation);
-		if (el1 == NULL) {
-			return LDB_ERR_CONSTRAINT_VIOLATION;
+		for (i = 0; i < msg->num_elements; i++) {
+			if (ldb_attr_cmp(msg->elements[i].name,
+					 "pwdLastSet") == 0) {
+				count++;
+				el = &msg->elements[i];
+			}
 		}
-		el2 = ldb_msg_find_element(msg, "pwdLastSet");
-		if (el2 == NULL) {
+		if (count != 1) {
 			return LDB_ERR_CONSTRAINT_VIOLATION;
 		}
-		if (el1 != el2) {
+
+		if (LDB_FLAG_MOD_TYPE(el->flags) == LDB_FLAG_MOD_DELETE) {
 			return LDB_ERR_CONSTRAINT_VIOLATION;
 		}
 
-- 
2.23.0

