High severity8.8NVD Advisory· Published Apr 7, 2026· Updated Apr 15, 2026
CVE-2026-27314
CVE-2026-27314
Description
Privilege escalation in Apache Cassandra 5.0 on an mTLS environment using MutualTlsAuthenticator allows a user with only CREATE permission to associate their own certificate identity with an arbitrary role, including a superuser role, and authenticate as that role via ADD IDENTITY.
Users are recommended to upgrade to version 5.0.7+, which fixes this issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.apache.cassandra:cassandra-allMaven | >= 5.0-alpha1, < 5.0.7 | 5.0.7 |
Affected products
1Patches
1b584a435970eDisallow binding an identity to a superuser when the user is a regular user
6 files changed · +106 −4
CHANGES.txt+1 −0 modified@@ -1,4 +1,5 @@ 5.0.7 + * Disallow binding an identity to a superuser when the user is a regular user (CASSANDRA-21219) * Fix ConcurrentModificationException in compaction garbagecollect (CASSANDRA-21065) * Dynamically skip sharding L0 when SAI Vector index present (CASSANDRA-19661) * Optionally force IndexStatusManager to use the optimized index status format (CASSANDRA-21132)
src/java/org/apache/cassandra/cql3/statements/AddIdentityStatement.java+6 −2 modified@@ -26,14 +26,15 @@ import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.RequestExecutionException; import org.apache.cassandra.exceptions.RequestValidationException; +import org.apache.cassandra.exceptions.UnauthorizedException; import org.apache.cassandra.service.ClientState; import org.apache.cassandra.transport.messages.ResultMessage; /** * Cqlsh statement to add identity into roles_to_identity table for storing authorized identities for mTLS connections. * Performs some checks before adding the identity to roles table. * - * EX: ADD IDENTITY 'testIdentity' TO ROLE 'testRole' + * <p>EX: ADD IDENTITY 'testIdentity' TO ROLE 'testRole' */ public class AddIdentityStatement extends AuthenticationStatement { @@ -51,7 +52,10 @@ public AddIdentityStatement(String identity, String role, boolean ifNotExists) @Override public void authorize(ClientState state) { - checkPermission(state, Permission.CREATE, state.getUser().getPrimaryRole()); + checkPermission(state, Permission.CREATE, RoleResource.root()); + + if (!state.getUser().isSuper() && DatabaseDescriptor.getRoleManager().isSuper(RoleResource.role(role))) + throw new UnauthorizedException("Only superusers can bind identities to a role with superuser status"); } @Override
src/java/org/apache/cassandra/cql3/statements/DropIdentityStatement.java+24 −1 modified@@ -21,10 +21,13 @@ import org.apache.cassandra.audit.AuditLogContext; import org.apache.cassandra.audit.AuditLogEntryType; import org.apache.cassandra.auth.Permission; +import org.apache.cassandra.auth.RoleResource; +import org.apache.cassandra.auth.Roles; import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.exceptions.InvalidRequestException; import org.apache.cassandra.exceptions.RequestExecutionException; import org.apache.cassandra.exceptions.RequestValidationException; +import org.apache.cassandra.exceptions.UnauthorizedException; import org.apache.cassandra.service.ClientState; import org.apache.cassandra.transport.messages.ResultMessage; @@ -46,7 +49,27 @@ public DropIdentityStatement(String identity, boolean ifExists) @Override public void authorize(ClientState state) { - checkPermission(state, Permission.DROP, state.getUser().getPrimaryRole()); + String roleForIdentity = DatabaseDescriptor.getRoleManager().roleForIdentity(identity); + + if (roleForIdentity == null) + { + checkPermission(state, Permission.DROP, RoleResource.root()); + } + else + { + // Check permission for the target role, i.e. we were granted permission to DROP ROLE + // for the target identity, this should allow us to drop the identity to role mapping + checkPermission(state, Permission.DROP, RoleResource.role(roleForIdentity)); + + if (!state.getUser().isSuper()) + { + // If the current user is a regular user and the target role is an admin role + // we disallow the operation. Only a superuser can remove an identity bound to + // a role with superuser status + if (Roles.hasSuperuserStatus(RoleResource.role(roleForIdentity))) + throw new UnauthorizedException("Only superusers can remove identity bindings from a role with superuser status"); + } + } } @Override
test/unit/org/apache/cassandra/auth/GrantAndRevokeTest.java+73 −0 modified@@ -487,6 +487,79 @@ public void testGrantOnVirtualKeyspaces() throws Throwable executeNet(ProtocolVersion.CURRENT, format("REVOKE SELECT PERMISSION ON KEYSPACE system_views FROM %s", user)); } + @Test + public void testAddIdentityPermissions() throws Throwable + { + useSuperUser(); + + executeNet(String.format("CREATE ROLE %s WITH LOGIN = TRUE AND password='%s'", user, pass)); + executeNet(String.format("GRANT CREATE ON ALL ROLES TO %s", user)); + + useUser(user, pass); + executeNet(String.format("ADD IDENTITY 'id1' TO ROLE '%s'", user)); + + // Should disallow binding an identity to a superuser role for regular users + assertUnauthorizedQuery("Only superusers can bind identities to a role with superuser status", + "ADD IDENTITY 'adminId' TO ROLE 'cassandra'"); + } + + @Test + public void testRemoveIdentityPermissionsWithSpecificRolePermission() throws Throwable + { + useSuperUser(); + + String simpleUser = "user_1"; + executeNet(String.format("CREATE ROLE %s WITH LOGIN = TRUE AND password='%s'", user, pass)); + executeNet(String.format("CREATE ROLE %s WITH LOGIN = TRUE AND password='%s'", simpleUser, pass)); + executeNet(String.format("ADD IDENTITY 'userId' TO ROLE '%s'", user)); + executeNet(String.format("ADD IDENTITY 'simpleUserId' TO ROLE '%s'", simpleUser)); + // allows user to drop simpleUser (including identity to role mappings) + executeNet(String.format("GRANT DROP ON ROLE %s TO %s", simpleUser, user)); + + useUser(user, pass); + executeNet("DROP IDENTITY 'simpleUserId'"); + // We should not be able to drop identities mapped for role "user" + assertUnauthorizedQuery("User user does not have sufficient privileges to perform the requested operation", + "DROP IDENTITY 'userId'"); + // Finally drop the "simpleUser" role + executeNet(String.format("DROP ROLE '%s'", simpleUser)); + } + + @Test + public void testRemoveIdentityPermissions() + { + useSuperUser(); + + executeNet(String.format("CREATE ROLE %s WITH LOGIN = TRUE AND password='%s'", user, pass)); + executeNet(String.format("GRANT DROP ON ALL ROLES TO %s", user)); + // Bind an identity to a superuser role + executeNet("ADD IDENTITY 'adminId' TO ROLE 'cassandra'"); + + useUser(user, pass); + + // Spin assert for effective auth changes. + Util.spinAssertEquals(false, () -> { + try + { + // Should disallow regular users from removing an identity binding from a superuser role + assertUnauthorizedQuery("Only superusers can remove identity bindings from a role with superuser status", + "DROP IDENTITY 'adminId'"); + } + catch (Throwable e) + { + return true; + } + return false; + }, 10); + + useSuperUser(); + // superusers can drop identities bound to superusers + executeNet("DROP IDENTITY 'adminId'"); + + // Should also be able to run an IF EXISTS query with a non-existent identity + executeNet("DROP IDENTITY IF EXISTS 'nonExistentUserId'"); + } + private void maybeReadSystemTables(boolean superuser) throws Throwable { if (superuser)
test/unit/org/apache/cassandra/cql3/CQLTester.java+1 −0 modified@@ -589,6 +589,7 @@ protected static void requireAuthentication() public void setup() { loadRoleStatement(); + loadIdentityStatement(); QueryProcessor.executeInternal(createDefaultRoleQuery()); } };
test/unit/org/apache/cassandra/cql3/statements/AddIdentityStatementTest.java+1 −1 modified@@ -138,7 +138,7 @@ public void testAddingNonExistentRole() } @Test - public void testUsersWithNoPrevilegesCannotAddIdentitiess() + public void testUsersWithNoPrivilegesCannotAddIdentities() { // Added user to roles table AuthenticatedUser authenticatedUser = new AuthenticatedUser("readwrite_user");
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- www.openwall.com/lists/oss-security/2026/04/07/7nvdMailing ListThird Party AdvisoryWEB
- github.com/advisories/GHSA-qxpc-96fq-wwmgghsaADVISORY
- lists.apache.org/thread/zrng82ddy4rpsmfyk582v6hqxcqrbz7fnvdMailing ListVendor AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2026-27314ghsaADVISORY
- github.com/apache/cassandra/commit/b584a435970e5125e1def5148d943c39569dc7afghsaWEB
- github.com/apache/cassandra/releases/tag/cassandra-5.0.7ghsaWEB
News mentions
3- Patch Tuesday - May 2026Rapid7 Blog · May 13, 2026
- Microsoft Patch Tuesday for May 2026 — Snort rules and prominent vulnerabilitiesCisco Talos Intelligence · May 12, 2026
- Microsoft May 2026 Patch Tuesday, (Tue, May 12th)SANS Internet Storm Center · May 12, 2026