VYPR
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.

PackageAffected versionsPatched versions
org.apache.cassandra:cassandra-allMaven
>= 5.0-alpha1, < 5.0.75.0.7

Affected products

1
  • cpe:2.3:a:apache:cassandra:*:*:*:*:*:*:*:*
    Range: >=5.0.0,<5.0.7

Patches

1
b584a435970e

Disallow binding an identity to a superuser when the user is a regular user

https://github.com/apache/cassandraFrancisco GuerreroFeb 17, 2026via ghsa
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

News mentions

3