From 58656a160c60d0155a3f25e426b0bac9a6792df5 Mon Sep 17 00:00:00 2001 From: Vault Automation Date: Wed, 29 Apr 2026 11:11:23 -0600 Subject: [PATCH] Fix MSSQL Default Revocation to use Least Privilege Metadata Query (#13528) (#14328) * Fix mssql lease revocation * Add changelog * Update comments Co-authored-by: sachin-chand01 --- changelog/_13528.txt | 3 +++ plugins/database/mssql/mssql.go | 27 +++++++++++++++++++++------ 2 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 changelog/_13528.txt diff --git a/changelog/_13528.txt b/changelog/_13528.txt new file mode 100644 index 0000000000..2ad2831af0 --- /dev/null +++ b/changelog/_13528.txt @@ -0,0 +1,3 @@ +```release-note:bug +database/mssql: Fix "sysadmin" requirement during lease revocation by replacing the undocumented `sp_msloginmappings` procedure with a granular metadata query. This allows the plugin to function with `VIEW ANY DEFINITION` instead of full `sysadmin` privileges. +``` \ No newline at end of file diff --git a/plugins/database/mssql/mssql.go b/plugins/database/mssql/mssql.go index 497619db31..82d677ae70 100644 --- a/plugins/database/mssql/mssql.go +++ b/plugins/database/mssql/mssql.go @@ -272,12 +272,17 @@ func (m *MSSQL) revokeUserDefault(ctx context.Context, username string) error { revokeStmts = append(revokeStmts, fmt.Sprintf("KILL %d;", sessionID)) } - // Query for database users using undocumented stored procedure for now since - // it is the easiest way to get this information; - // we need to drop the database users before we can drop the login and the role - // This isn't done in a transaction because even if we fail along the way, - // we want to remove as much access as possible - stmt, err := db.PrepareContext(ctx, "EXEC master.dbo.sp_msloginmappings @p1;") + // selectUserSQL identifies database-level users associated with a server login. + // We use a join on sys.server_principals and sys.database_principals instead + // of the undocumented sp_msloginmappings procedure. This allows revocation + // to function with 'VIEW ANY DEFINITION' instead of 'sysadmin' privileges, + // supporting "Least Privilege" security models. + // + // Note: This identifies users within the current database context. We must + // drop these database users before the server-level login can be removed. + // We execute this outside of a transaction to ensure we revoke as much + // access as possible, even if a single DROP statement fails. + stmt, err := db.PrepareContext(ctx, selectUserSQL) if err != nil { return err } @@ -443,3 +448,13 @@ ALTER LOGIN [{{username}}] WITH PASSWORD = '{{password}}' const alterUserContainedSQL = ` ALTER USER [{{username}}] WITH PASSWORD = '{{password}}' ` + +const selectUserSQL = ` SELECT + l.name AS LoginName, + DB_NAME() AS DBName, + u.name AS UserName, + 'Member' AS UserOrAlias + FROM sys.server_principals l + JOIN sys.database_principals u ON l.sid = u.sid + WHERE l.name = @p1; +`