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"));