This is an automated email from the ASF dual-hosted git repository.

CRZbulabula pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 9cebc313f83 Fix audit vulnerabilities. (#17573)
9cebc313f83 is described below

commit 9cebc313f839f5ea647b237648e32d9805c80002
Author: wenyanshi-123 <[email protected]>
AuthorDate: Wed Apr 29 09:18:36 2026 +0800

    Fix audit vulnerabilities. (#17573)
---
 .../org/apache/iotdb/db/it/auth/IoTDBAuthIT.java   | 13 ++++++
 .../iotdb/db/it/auth/IoTDBRelationalAuthIT.java    | 13 ++++++
 .../org/apache/iotdb/db/auth/AuthorityChecker.java |  2 +-
 .../relational/security/ITableAuthCheckerImpl.java |  4 ++
 .../org/apache/iotdb/commons/auth/entity/User.java | 19 ++++++++
 .../iotdb/commons/auth/user/BasicUserManager.java  | 25 ++++++++--
 .../org/apache/iotdb/commons/utils/AuthUtils.java  | 53 +++++++++++++++++++++-
 .../apache/iotdb/commons/utils/AuthUtilsTest.java  | 32 +++++++++++++
 8 files changed, 154 insertions(+), 7 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java 
b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java
index 5ec674b2a37..1d451edf973 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java
@@ -1627,4 +1627,17 @@ public class IoTDBAuthIT {
       fail(e.getMessage());
     }
   }
+
+  @Test
+  public void testUserNameMustNotStartWithDoubleUnderscore() throws 
SQLException {
+    try (Connection adminCon = EnvFactory.getEnv().getConnection();
+        Statement adminStmt = adminCon.createStatement()) {
+      Assert.assertThrows(
+          SQLException.class, () -> adminStmt.execute("CREATE USER __badx 
'password123456'"));
+      adminStmt.execute("CREATE USER gooduser 'password123456'");
+      Assert.assertThrows(
+          SQLException.class, () -> adminStmt.execute("ALTER USER gooduser 
RENAME TO __badx"));
+      adminStmt.execute("DROP USER gooduser");
+    }
+  }
 }
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java
index 960c35f217a..dfb69c4a7c9 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java
@@ -569,6 +569,19 @@ public class IoTDBRelationalAuthIT {
     }
   }
 
+  @Test
+  public void testUserNameMustNotStartWithDoubleUnderscore() throws 
SQLException {
+    try (Connection adminCon = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement adminStmt = adminCon.createStatement()) {
+      Assert.assertThrows(
+          SQLException.class, () -> adminStmt.execute("create user __badx 
'password123456'"));
+      adminStmt.execute("create user gooduser 'password123456'");
+      Assert.assertThrows(
+          SQLException.class, () -> adminStmt.execute("ALTER USER gooduser 
RENAME TO __badx"));
+      adminStmt.execute("DROP USER gooduser");
+    }
+  }
+
   @Test
   public void testAudit() throws SQLException {
     try (Connection adminCon = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
index eef9d9277b4..164baa4f198 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
@@ -85,7 +85,7 @@ public class AuthorityChecker {
   public static final TSStatus SUCCEED = new 
TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
 
   public static final int INTERNAL_AUDIT_USER_ID = 4;
-  public static final String INTERNAL_AUDIT_USER = "__internal_auditor";
+  public static final String INTERNAL_AUDIT_USER = 
User.BUILTIN_INTERNAL_AUDIT_LOG_USERNAME;
 
   public static String ANY_SCOPE = "any";
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java
index 16c32880c8e..d20e7c88c31 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java
@@ -172,6 +172,10 @@ public class ITableAuthCheckerImpl implements 
ITableAuthChecker {
   }
 
   public static void checkCanSelectAuditTable(IAuditEntity auditEntity) {
+    if (AuthorityChecker.INTERNAL_AUDIT_USER_ID == auditEntity.getUserId()
+        || 
AuthorityChecker.INTERNAL_AUDIT_USER.equals(auditEntity.getUsername())) {
+      return;
+    }
     String userName = auditEntity.getUsername();
     if (AuthorityChecker.SUPER_USER_ID != auditEntity.getUserId()
         && !AuthorityChecker.checkSystemPermission(userName, 
PrivilegeType.AUDIT)) {
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java
index d2907846738..0bebeaf8e06 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java
@@ -42,6 +42,25 @@ public class User extends Role {
   public static final long INTERNAL_SECURITY_ADMIN = 2;
   public static final long INTERNAL_AUDIT_ADMIN = 3;
 
+  /**
+   * Login name of the built-in internal audit log writer. Always starts with 
{@link
+   * #BUILTIN_USERNAME_PREFIX}.
+   */
+  public static final String BUILTIN_INTERNAL_AUDIT_LOG_USERNAME = 
"__internal_auditor";
+
+  /**
+   * User names with this prefix are reserved for system-style built-in 
accounts. New user creation
+   * and {@code RENAME USER} targets must reject this prefix; accounts that 
already use such a name
+   * keep working until renamed. See {@link
+   * org.apache.iotdb.commons.utils.AuthUtils#validateNewUserUsername(String)}.
+   */
+  public static final String BUILTIN_USERNAME_PREFIX = "__";
+
+  /** True if the name has the system-reserved {@link 
#BUILTIN_USERNAME_PREFIX} prefix. */
+  public static boolean isSystemReservedUsername(String username) {
+    return username != null && username.startsWith(BUILTIN_USERNAME_PREFIX);
+  }
+
   private long userId = -1;
 
   private String password;
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java
index 2b97a30108f..5f28875e580 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java
@@ -153,7 +153,7 @@ public abstract class BasicUserManager extends 
BasicRoleManager {
       String username, String password, boolean validCheck, boolean 
enableEncrypt)
       throws AuthException {
     if (validCheck) {
-      validCheck(username, password, enableEncrypt);
+      validCheckForNewUser(username, password, enableEncrypt);
     }
 
     User user = this.getEntity(username);
@@ -183,7 +183,7 @@ public abstract class BasicUserManager extends 
BasicRoleManager {
       String username, String password, long userId, boolean validCheck, 
boolean enableEncrypt)
       throws AuthException {
     if (validCheck) {
-      validCheck(username, password, enableEncrypt);
+      validCheckForBuiltinUser(username, password, enableEncrypt, userId);
     }
     User user = this.getEntity(username);
     if (user != null) {
@@ -201,7 +201,7 @@ public abstract class BasicUserManager extends 
BasicRoleManager {
     }
   }
 
-  private void validCheck(String username, String password, boolean 
enableEncrypt)
+  private void validCheckForNewUser(String username, String password, boolean 
enableEncrypt)
       throws AuthException {
     if 
(!CommonDescriptor.getInstance().getConfig().getDefaultAdminName().equals(username))
 {
       if (username.equals(password)
@@ -209,7 +209,22 @@ public abstract class BasicUserManager extends 
BasicRoleManager {
         throw new AuthException(
             TSStatusCode.ILLEGAL_PASSWORD, "Password cannot be the same as 
user name");
       }
-      AuthUtils.validateUsername(username);
+      AuthUtils.validateNewUserUsername(username);
+      if (enableEncrypt) {
+        AuthUtils.validatePassword(password);
+      }
+    }
+  }
+
+  private void validCheckForBuiltinUser(
+      String username, String password, boolean enableEncrypt, long userId) 
throws AuthException {
+    if 
(!CommonDescriptor.getInstance().getConfig().getDefaultAdminName().equals(username))
 {
+      if (username.equals(password)
+          && 
CommonDescriptor.getInstance().getConfig().isEnforceStrongPassword()) {
+        throw new AuthException(
+            TSStatusCode.ILLEGAL_PASSWORD, "Password cannot be the same as 
user name");
+      }
+      AuthUtils.validateInternalBuiltinUsername(username, userId);
       if (enableEncrypt) {
         AuthUtils.validatePassword(password);
       }
@@ -242,7 +257,7 @@ public abstract class BasicUserManager extends 
BasicRoleManager {
   }
 
   public void renameUser(String username, String newUsername) throws 
AuthException {
-    AuthUtils.validateName(newUsername);
+    AuthUtils.validateNewUserUsername(newUsername);
     User user = this.getEntity(username);
     if (user == null) {
       throw new AuthException(
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
index 67c8ab1a80c..1aae1909ef2 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
@@ -21,6 +21,7 @@ package org.apache.iotdb.commons.utils;
 import org.apache.iotdb.commons.auth.AuthException;
 import org.apache.iotdb.commons.auth.entity.PathPrivilege;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
+import org.apache.iotdb.commons.auth.entity.User;
 import org.apache.iotdb.commons.conf.CommonDescriptor;
 import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.commons.path.PartialPath;
@@ -144,7 +145,7 @@ public class AuthUtils {
   }
 
   /**
-   * Validate username
+   * Validate username string (length, allowed characters) only, without 
{@code __} rules.
    *
    * @param username username
    * @throws AuthException contains message why username is invalid
@@ -153,6 +154,56 @@ public class AuthUtils {
     validateName(username);
   }
 
+  /**
+   * Usernames for built-in users created with a fixed id via {@code 
tryToCreateBuiltinUser}: the
+   * default superuser (id {@link IoTDBConstant#SUPER_USER_ID}) and the three 
separation-of-duties
+   * admins (ids {@link User#INTERNAL_SYSTEM_ADMIN}, {@link 
User#INTERNAL_SECURITY_ADMIN}, {@link
+   * User#INTERNAL_AUDIT_ADMIN}) may use any valid name; all other internal 
ids must use a name with
+   * prefix {@link User#BUILTIN_USERNAME_PREFIX}.
+   */
+  public static void validateInternalBuiltinUsername(String username, long 
userId)
+      throws AuthException {
+    validateName(username);
+    if (userId == IoTDBConstant.SUPER_USER_ID) {
+      return;
+    }
+    if (userId == User.INTERNAL_SYSTEM_ADMIN
+        || userId == User.INTERNAL_SECURITY_ADMIN
+        || userId == User.INTERNAL_AUDIT_ADMIN) {
+      return;
+    }
+    if (!User.isSystemReservedUsername(username)) {
+      throw new AuthException(
+          TSStatusCode.ILLEGAL_PASSWORD,
+          "Internal user names (except the default superuser and 
separation-of-duties admins) must "
+              + "start with \""
+              + User.BUILTIN_USERNAME_PREFIX
+              + "\"");
+    }
+  }
+
+  /**
+   * Validate a login name that is being <b>newly assigned</b>: {@code CREATE 
USER}, or the target
+   * name of {@code RENAME USER}. Same as {@link #validateName(String)} plus 
disallows the {@link
+   * User#BUILTIN_USERNAME_PREFIX} prefix. Existing accounts that already use 
a {@code __} name are
+   * unchanged; they may keep using it, but cannot {@code RENAME} to another 
{@code __}-prefixed
+   * name.
+   *
+   * @param username username
+   * @throws AuthException contains message why username is invalid
+   */
+  public static void validateNewUserUsername(String username) throws 
AuthException {
+    validateName(username);
+    if (User.isSystemReservedUsername(username)) {
+      throw new AuthException(
+          TSStatusCode.ILLEGAL_PASSWORD,
+          "User names starting with \""
+              + User.BUILTIN_USERNAME_PREFIX
+              + "\" are reserved for system use and cannot be used for new 
users or as a rename "
+              + "target");
+    }
+  }
+
   /**
    * Validate role name
    *
diff --git 
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java
 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java
index 9e79624bc40..7809d005e98 100644
--- 
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java
+++ 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java
@@ -22,6 +22,8 @@ package org.apache.iotdb.commons.utils;
 import org.apache.iotdb.commons.auth.AuthException;
 import org.apache.iotdb.commons.auth.entity.PathPrivilege;
 import org.apache.iotdb.commons.auth.entity.PrivilegeType;
+import org.apache.iotdb.commons.auth.entity.User;
+import org.apache.iotdb.commons.conf.IoTDBConstant;
 import org.apache.iotdb.commons.exception.IllegalPathException;
 import org.apache.iotdb.commons.path.PartialPath;
 
@@ -59,6 +61,36 @@ public class AuthUtilsTest {
                     + "                + 
\"jkzxcvbnmqwertyuioasdfghjklzxcvbnm"));
   }
 
+  @Test
+  public void authUtilsTest_ReservedBuiltinUsernamePrefix() throws 
AuthException {
+    
Assert.assertTrue(User.isSystemReservedUsername(User.BUILTIN_INTERNAL_AUDIT_LOG_USERNAME));
+    Assert.assertTrue(User.isSystemReservedUsername("__any_builtin_style"));
+    Assert.assertFalse(User.isSystemReservedUsername("_single_underscore"));
+    Assert.assertFalse(User.isSystemReservedUsername("user__suffix"));
+
+    // validateUsername: format only (e.g. builtin init path may use this)
+    AuthUtils.validateUsername(User.BUILTIN_INTERNAL_AUDIT_LOG_USERNAME);
+    AuthUtils.validateUsername("__abcd");
+
+    // CREATE USER and RENAME USER target: "__" prefix is not allowed
+    Assert.assertThrows(
+        AuthException.class,
+        () -> 
AuthUtils.validateNewUserUsername(User.BUILTIN_INTERNAL_AUDIT_LOG_USERNAME));
+    Assert.assertThrows(AuthException.class, () -> 
AuthUtils.validateNewUserUsername("__abcd"));
+  }
+
+  @Test
+  public void authUtilsTest_InternalBuiltinUsername() throws AuthException {
+    AuthUtils.validateInternalBuiltinUsername("sys_admin", 
User.INTERNAL_SYSTEM_ADMIN);
+    AuthUtils.validateInternalBuiltinUsername("security_admin", 
User.INTERNAL_SECURITY_ADMIN);
+    AuthUtils.validateInternalBuiltinUsername("audit_admin", 
User.INTERNAL_AUDIT_ADMIN);
+    AuthUtils.validateInternalBuiltinUsername("root", 
IoTDBConstant.SUPER_USER_ID);
+
+    Assert.assertThrows(
+        AuthException.class, () -> 
AuthUtils.validateInternalBuiltinUsername("nointernal", 4L));
+    AuthUtils.validateInternalBuiltinUsername("__internal_auditor", 4L);
+  }
+
   @Test
   public void authUtilsTest_PrivilegeGrantRevokeCheck() throws 
IllegalPathException {
     PartialPath path = new PartialPath(new String("root.t1"));

Reply via email to