From 80e78f26b570e1effdaa3a743e25b37b775fd6a6 Mon Sep 17 00:00:00 2001
From: Stefan Metzmacher <metze@samba.org>
Date: Thu, 23 Apr 2026 18:56:21 +0200
Subject: [PATCH 20/27] CVE-2026-4408: lib/util: introduce
 strstr_for_invalid_account_characters()

This splits out the logic from samaccountname_bad_chars_check()
in source4/dsdb/samdb/ldb_modules/samldb.c, this will be used
in other places soon.

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

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
---
 lib/util/samba_util.h | 11 ++++++++++-
 lib/util/util_str.c   | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 1 deletion(-)

--- a/lib/util/samba_util.h
+++ b/lib/util/samba_util.h
@@ -299,7 +299,16 @@ _PUBLIC_ bool set_boolean(const char *bo
 _PUBLIC_ bool conv_str_bool(const char * str, bool * val);
 
 /**
- * Convert a size specification like 16K into an integral number of bytes. 
+ * Returns a pointer to the first invalid character in name.
+ *
+ * Passing a NULL pointer as name is not allowed!
+ *
+ * This returns NULL for a valid account name.
+ **/
+_PUBLIC_ const char *strstr_for_invalid_account_characters(const char *name);
+
+/**
+ * Convert a size specification like 16K into an integral number of bytes.
  **/
 _PUBLIC_ bool conv_str_size_error(const char * str, uint64_t * val);
 
--- a/lib/util/util_str.c
+++ b/lib/util/util_str.c
@@ -316,3 +316,41 @@ _PUBLIC_ int memcmp_const_time(const voi
 
 	return sum != 0;
 }
+
+_PUBLIC_ const char *strstr_for_invalid_account_characters(const char *name)
+{
+	/*
+	 * Return a pointer to the first invalid character in the
+	 * sAMAccountName, or NULL if the whole name is valid.
+	 *
+	 * The rules here are based on
+	 *
+	 * https://social.technet.microsoft.com/wiki/contents/articles/11216.active-directory-requirements-for-creating-objects.aspx
+	 */
+	size_t i;
+
+	for (i = 0; name[i] != '\0'; i++) {
+		uint8_t c = name[i];
+		const char *p = NULL;
+
+		if (iscntrl(c)) {
+			return &name[i];
+		}
+
+		p = strchr("\"[]:;|=+*?<>/\\,", c);
+		if (p != NULL) {
+			return &name[i];
+		}
+	}
+
+	if (i == 0) {
+		return &name[i];
+	}
+
+	if (name[i - 1] == '.') {
+		i -= 1;
+		return &name[i];
+	}
+
+	return NULL;
+}
