How enable non-sysadmin accounts to execute the "DBCC CHECKIDENT"? - sql-server

Before each DAO test I clean my database and I need to reset the identity value of some tables. I've created the following stored procedure:
CREATE PROCEDURE SET_IDENTITY
#pTableName varchar(120),
#pSeedValue int
AS
BEGIN
DBCC CHECKIDENT(#pTableName, RESEED, #pSeedValue);
END
My problem is I need to call this stored procedure with a "normal" user. In order works, This user cannot be member of: sysadmin, db_owner, db_ddladmin.
I've tried with:
a) CREATE PROCEDURE WITH EXECUTE AS OWNER
b) EXECUTE AS USER = 'sa' before call DBCC CHECKIDENT
But in both cases I got back:
The server principal sa is not able to access the database my_db_name under the current security context.
I'm using Microsoft SQL Server Express (64-bit) 11.0.2100.60
Thank you in advance,
Abel

This is easy enough to do, but generally speaking you shouldn't need to reset identity values each time. Specific identity values shouldn't matter, so the only concern should be potentially reaching the max value due to repeated testing. And in that case I would not recommend resetting each time as it is also a good test to let IDs reach high values so you can make sure that all code paths handle them properly and find areas that don't before your users do ;-).
That being said, all you need to do (assuming this is localized to a single DB) is create an Asymmetric Key, then create a User from it, then add that User to the db_ddladmin fixed Database Role, and finally sign your Stored Procedure with that same Asymmetric Key.
The following example illustrates this behavior:
USE [tempdb];
CREATE TABLE dbo.CheckIdent
(
[ID] INT NOT NULL IDENTITY(1, 1) CONSTRAINT [PK_CheckIdentity] PRIMARY KEY,
[Something] VARCHAR(50)
);
EXEC(N'
CREATE PROCEDURE dbo.SET_IDENTITY
#pTableName sysname,
#pSeedValue int
AS
BEGIN
DBCC CHECKIDENT(#pTableName, RESEED, #pSeedValue);
END;
');
CREATE USER [MrNobody] WITHOUT LOGIN;
GRANT EXECUTE ON dbo.SET_IDENTITY TO [MrNobody];
-------
EXECUTE AS USER = N'MrNobody';
SELECT SESSION_USER AS [CurrentUser], ORIGINAL_LOGIN() AS [OriginalLogin];
EXEC dbo.SET_IDENTITY N'dbo.CheckIdent', 12;
/*
Msg 2557, Level 14, State 5, Procedure SET_IDENTITY, Line 7 [Batch Start Line 30]
User 'MrNobody' does not have permission to run DBCC CHECKIDENT for object 'CheckIdent'.
*/
REVERT;
SELECT SESSION_USER AS [CurrentUser], ORIGINAL_LOGIN() AS [OriginalLogin];
-------
CREATE ASYMMETRIC KEY [DdlAdminPermissionsKey]
WITH ALGORITHM = RSA_2048
ENCRYPTION BY PASSWORD = 'not_a_good_password';
CREATE USER [DdlAdminPermissions]
FROM ASYMMETRIC KEY [DdlAdminPermissionsKey];
ALTER ROLE [db_ddladmin] ADD MEMBER [DdlAdminPermissions];
ADD SIGNATURE
TO dbo.SET_IDENTITY
BY ASYMMETRIC KEY [DdlAdminPermissionsKey]
WITH PASSWORD = 'not_a_good_password';
-------
EXECUTE AS USER = N'MrNobody';
SELECT SESSION_USER AS [CurrentUser], ORIGINAL_LOGIN() AS [OriginalLogin];
EXEC dbo.SET_IDENTITY N'dbo.CheckIdent', 12;
-- Success!
REVERT;
SELECT SESSION_USER AS [CurrentUser], ORIGINAL_LOGIN() AS [OriginalLogin];
Other minor notes:
You cannot execute as User = sa since sa is a Login (server level) and not a User (database level). You can use EXECUTE AS LOGIN = 'sa'; but that then requires IMPERSONATE permission and is a security hole since the non-privileged Login can run EXECUTE AS LOGIN = 'sa' whenever they want. So don't do that.
For variables / parameters that hold names of SQL Server objects, indexes, etc, you should use NVARCHAR and not VARCHAR. Most internal names use sysname which is a system alias for NVARCHAR(128), so sysname is usually the preferred datatype.

Caller has to own schema or be db_owner:
From doc: DBCC CHECKIDENT
Permissions
Caller must own the schema that contains the table, or be a member of the sysadmin fixed server role, the db_owner fixed database role, or the db_ddladmin fixed database role.
LiveDemo

Related

SQL Server Permission Chaining

I have the following issue:
I have two different databases, db1 and db2. I have an application that loads data into db2 or db3. db1 has a few tables that the application uses to determine behavior, including which db the application should load data into.
Users need to have write access to db1 to operate the application (there is a console application that writes to tables in db1, operating with windows authentication).
Users should not have DML privileges to db2 and db3, with the exception of a few predetermined operations. We grant AD groups database roles to control access from and organization perspective. Specifically, I'm trying to build a stored procedure in db1 that operators can use to reverse data loaded to db2 or db3 with appropriate logging.
I'm attempting to use create proc ... execute as owner to accomplish this, but it does not seem to be working when I try to hit tables in db2/db3 (I'm thinking that "execute as owner" operates on db level users an not server level logins?). The following causes a permission error stating that the owner (myself) does not have permissions to db2/db3.
use db1
go
create proc dbo.wrapper #recordid int
as begin
/*
capturing user
*/
declare #usr varchar(255) = SUSER_SNAME()
exec dbo.inner #usr , #recordid
end
use db1
go
create proc dbo.inner #usr varchar(255), #recordid int
with execute as owner
as begin
/*
logic to determine whether to update db2 or db3 goes here
*/
insert db2.rolled_back
select * , #usr from db2.transactions where id = #recordid
delete from db2.transactions where id = #recordid
insert db3.rolled_back
select * , #usr from db3.transactions where id = #recordid
delete from db3.transactions where id = #recordid
end
Is there a way to get this to work? I've heard that certificate signing could do this, does anyone have any experience using certificate users. Our DBA's would rather not have to maintain certificates, so if there is a way to get this to work without certificates that would be best.
Any advice would be helpful.
Thank You!
I'm going to cover the cross database chaining side of thing here. note that there are certainly security considerations when using this method. For example someone with permissions to create objects in one database can give themselves access to data in another database with the owner, when they themselves have no access to the other database. The security concerns, however, are out of scope of this answer.
Firstly, let's create a couple of test databases.
USE master;
GO
CREATE DATABASE Chain1;
CREATE DATABASE Chain2;
Now I'm going to CREATE a LOGIN, which is disable and make that the owner of these 2 databases. The databases having the same owner is important, as otherwise the chaining won't work.
CREATE LOGIN ChainerOwner WITH PASSWORD = N'SomeSecurePassword123';
ALTER LOGIN ChainerOwner DISABLE;
GO
ALTER AUTHORIZATION ON DATABASE::Chain1 TO ChainerOwner;
ALTER AUTHORIZATION ON DATABASE::Chain2 TO ChainerOwner;
I'm also going to create a LOGIN which we're going to use to test on:
CREATE LOGIN SomeUser WITH PASSWORD = N'SomeSecurePassword123';
Great, now I can create a few objects; a table in Chain1, a PROCEDURE in Chain2 that accesses the TABLE, and a USER in both databases for SomeUser. In Chain1 the USER will be given no permissions, and in Chain2 the user will be given the permsision to EXECUTE the PROCEDURE:
USE Chain1;
GO
CREATE TABLE dbo.SomeTable (I int IDENTITY,
S varchar(10));
INSERT INTO dbo.SomeTable (S)
VALUES ('abc'),
('xyz');
GO
CREATE USER SomeUser FOR LOGIN SomeUser;
GO
USE Chain2;
GO
CREATE PROC dbo.CrossDBProc #I int AS
BEGIN
SELECT I,
S
FROM Chain1.dbo.SomeTable
WHERE I = #I;
END;
GO
CREATE USER SomeUser FOR LOGIN SomeUser;
GO
GRANT EXECUTE ON dbo.CrossDBProc TO SomeUser;
GO
Great, all the objects are created, now let's try to EXECUTE that PROCEDURE:
EXECUTE AS LOGIN = 'SomeUser';
GO
EXEC dbo.CrossDBProc 1; --This fails
GO
REVERT;
GO
This fails, with a permission error:
The SELECT permission was denied on the object 'SomeTable', database 'Chain1', schema 'dbo'.
This is expected, as there is no ownership chaining. let's, therefore enable that now.
USE master;
GO
ALTER DATABASE Chain1 SET DB_CHAINING ON;
ALTER DATABASE Chain2 SET DB_CHAINING ON;
Now if I try the same again, the same SQL works:
USE Chain2;
GO
EXECUTE AS LOGIN = 'SomeUser';
GO
EXEC dbo.CrossDBProc 1; --This now works
GO
REVERT;
GO
This successfully returns the result set
I
S
1
abc
So, yes you can chain cross database, but it requires some set up, and (again) there are security considerations you need think about.
Clean up:
USE master;
GO
DROP DATABASE Chain1;
DROP DATABASE Chain2;
GO
DROP LOGIN ChainerOwner;
DROP LOGIN SomeUser;

SQL Server: Is it possible to synchronize user GUIDs between two databases if the users are not tied to a login?

Our project uses SQL Server database users without logins to implement security and row-level filtering.
We have implemented a very basic form of mirroring in which the transactional database is backed up and restored nightly to a second “mirror” copy. A web service pulls data from the mirror.
However, we need to log web service requests in the transactional database so that they are not wiped out when the next mirror is restored.
We attempted to implement this by replacing the log tables in the mirror with synonyms pointing at the "real" tables in the transactional database.
However, attempts to write to the synonyms invariably fail with error messages such as:
The server principal "" is not able to access the database "" under the current security context
I’m guessing this happens because, during restore, the users are re-created and assigned new GUIDs?
I've found lots of answers that talk about re-connecting a database user to a SQL Server login using sp_change_users_login or ALTER USER, but these solutions don't seem applicable, since these database users don't have logins.
Is there some way to ensure that users in the mirrored database are recognized as the same user in the transactional database if their usernames are the same?
Thanks!
Yes, when you CREATE a USER (or LOGIN) you can define the SID when you create it:
USE DB1;
GO
CREATE USER SampleUser WITHOUT LOGIN WITH SID = 0x010500000000000903000000F759D99F7F71EC459908C0A30B39056C;
USE DB2;
GO
CREATE USER SampleUser WITHOUT LOGIN WITH SID = 0x010500000000000903000000F759D99F7F71EC459908C0A30B39056C;
Is there some way to ensure that users in the mirrored database are
recognized as the same user in the transactional database if their
usernames are the same?
The answer is:No
Users without logins are database principals only. For any users, in different dbs, to be "one" they need to "point" to the same server principal: login. It is the login that "connects" database users.
I’m guessing this happens because, during restore, the users are
re-created and assigned new GUIDs?
The SIDs (Security IDentifiers) of the database users do not change when the db is restored.
The fact that the sids do not change but remain the same, requires the actions you have found (sp_change_users_login or ALTER USER) when a database is restored to another server.
3 options for cross database actions:
cross database ownership chaining
impersonation
module signing
I cannot tell if crossdb chaining will work, since the loginless users do not have any security essence when they leave their database. Outside their database, loginless users have no security attributes, they lose their "identity".
On the other hand, impersonation and module signing can work, because the security attributes are warranted by another principal.
Following is a "simplistic" poc, for db users without login, performing cross db actions with sql-modules signed by an asymmetric key. There are plenty of examples online for module signing with certificates and the workings are the same. Most articles you'll find online, execute the signed modules in the scope of a server principal (exec as login, exec signed_module) while the loginless users have no server level existence.
Signed modules need to be executed by a trusted entity for the signature to take effect(a login is by definition trusted) and for loginless users their trustiness comes from their own db. For that, the db where the loginless users reside must be TRUSTWORTHY ON.
In the poc, there is a signed multiline table valued function in the mirrordb which is used for selecting data from the transactional db. The function is the base for a view (views cannot be signed). The view is the target of DML. The actual dml action(s) are performed by signed INSTEAD OF triggers.
You need to generate&adjust the path to the asymmetric key before executing the script.
Impersonation could work the same way(take away the signing related actions and create each module with EXECUTE AS)
create database transactionaldb
go
create database mirrordb
go
--target table in transactionaldb
use transactionaldb
go
create table targettable
(
id int identity,
username nvarchar(128) default(user_name()),
somevalue varchar(10),
thedate datetime default(getdate())
)
go
--mirror db, userwithout login
use mirrordb
go
create user userwithoutlogin without login
go
--synonym, just for testing
create synonym targettablesynonym for transactionaldb..targettable
go
--for testing, grant permissions on synonym to loginless user
grant select, insert, update, delete on targettablesynonym to userwithoutlogin
go
--switch to loginless user and select from synonym
execute as user = 'userwithoutlogin'
go
select * from targettablesynonym --The server principal "S-1-2-3-4..." is not able to access the database "transactionaldb" under the current security context.
go
--revert
revert
go
/*
1) cross db ownership chaining
2) impersonation
3) module signing (below)
*/
--module signing, asymmetric key
--create a strong key/name file using sn.exe : sn -k 2048 c:\testdata\asymkeytest.snk //use key size 2048 for sql2016 on.
--the same for transactionaldb
use transactionaldb
go
--master key transactionaldb
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'M#st3rkeyTransactional'
GO
CREATE ASYMMETRIC KEY asymkeytest
AUTHORIZATION dbo
FROM FILE = 'c:\testdata\asymkeytest.snk';
GO
create user asymkeytransactionaldbuser from asymmetric key asymkeytest
go
--grant permissions on targettable to asymkeytransactionaldbuser
grant select, insert, update, delete on targettable to asymkeytransactionaldbuser;
go
use mirrordb
go
--master key mirrordb
CREATE MASTER KEY ENCRYPTION BY PASSWORD = 'M#st3rkeyMirror'
GO
CREATE ASYMMETRIC KEY asymkeytest
AUTHORIZATION dbo
FROM FILE = 'c:\testdata\asymkeytest.snk';
GO
--a db user from the asymkey? not really needed
--create user asymkeymirrordbuser from asymmetric key asymkeytest
go
select is_master_key_encrypted_by_server, *
from sys.databases
where name in ('transactionaldb', 'mirrordb');
go
use mirrordb
go
--a function in mirror which reads the table from transactional
create or alter function dbo.fnreadtargettablefromtransactionaldb()
returns #result table
(
id int,
username nvarchar(128),
somevalue varchar(10),
thedate datetime
)
as
begin
insert into #result(id, username, somevalue, thedate)
select id, username, somevalue, thedate
from transactionaldb.dbo.targettable;
return;
end
go
--grant select on loginless user
grant select on fnreadtargettablefromtransactionaldb to userwithoutlogin;
go
--switch to loginless user and select from function
execute as user = 'userwithoutlogin'
go
select * from fnreadtargettablefromtransactionaldb(); --The server principal "S-1-2-3-4..." is not able to access the database "transactionaldb" under the current security context.
go
--revert
revert
go
--sign the function with the asymmetric key
add signature to fnreadtargettablefromtransactionaldb by ASYMMETRIC KEY asymkeytest;
--... after signing
execute as user = 'userwithoutlogin'
go
select * from fnreadtargettablefromtransactionaldb(); --The server principal "S-1-2-3-4..." is not able to access the database "transactionaldb" under the current security context.
go
--revert
revert
go
--the signed module/function is accessing the transactionaldb but it is NOT trusted
--it could be trusted if:
-- a. it was called in the context of a server principal (login, by definition it is trusted)
-- b. if the source db of the signed module is trustful
--make the source db of the signed module trustful
alter database mirrordb set trustworthy on;
--test again
execute as user = 'userwithoutlogin'
go
--synonym (needs permissions now, at the destination)
select * from targettablesynonym
--signed function, working, permissions from the asymmetric key
select * from fnreadtargettablefromtransactionaldb(); --works
go
--revert
revert
go
use mirrordb
go
--complete interface
create or alter view transactiontableview
as
select *
from fnreadtargettablefromtransactionaldb()
go
--view permissions
grant select, insert, update, delete on transactiontableview to userwithoutlogin;
go
--instead of insert trigger
create or alter trigger insteadofinsertonview on transactiontableview instead of insert
as
begin
set nocount on;
if not exists(select * from inserted)
begin
return;
end
insert into transactionaldb.dbo.targettable(username, somevalue, thedate)
select user_name(), somevalue, thedate
from inserted;
end
go
--sign
add signature to insteadofinsertonview by ASYMMETRIC KEY asymkeytest;
go
execute as user = 'userwithoutlogin'
go
insert into transactiontableview(somevalue)
select col
from (values('one'), ('2'), ('3')) as t(col);
select * from transactiontableview
go
--revert
revert
go
/***********************************************/
--check the security token of a signed module when db trustworthy is off
alter database mirrordb set trustworthy off;
go
--create a proc
create or alter procedure showsecuritytokens
as
begin
select 'mirror_db' as dbname, *
from mirrordb.sys.user_token
union all
select 'transactional_db' as dbname, *
from transactionaldb.sys.user_token;
end
go
--sign the proc for accessing transactionaldb info
add signature to showsecuritytokens by ASYMMETRIC KEY asymkeytest;
go
--permissions
grant execute on showsecuritytokens to userwithoutlogin
go
--enable guest for transactionaldb
use transactionaldb
go
grant connect to guest
go
use mirrordb
go
--switch to loginless user
execute as user = 'userwithoutlogin'
go
exec showsecuritytokens
--when from an untrusted source: the signed module does not get the GRANTs of the asymmetric key (at the destination)
/*
thedb_______________principal_id__name_________________________type___________________________usage
transactional_db____5_____________asymkeytransactionaldbuser___USER MAPPED TO ASYMMETRIC KEY__DENY ONLY
*/
go
--revert
revert
go
--cleanup
/*
use master
go
drop database transactionaldb
go
drop database mirrordb
go
*/

"Execute as self" vs cross database views

SQL Server 2016. There's a view in the database A that makes a selection from a table in another database, B:
use A
go
create view TheView as
select * from B.dbo.SomeTable
I have dbo access to the B database, and I can query the view all I want:
select * from TheView -- Works as expected
Now I've created a procedure with EXECUTE AS SELF clause, hoping it will execute as me:
use A
go
create proc dbo.f
with execute as self
as
select * from TheView
When I run it, I get the following:
The server principal "ACME\seva" is not able to access the database "B" under the current security context.
If I remove the execute as clause, the procedure runs as expected.
I'm connecting with a Windows domain account, using integrated security.
What am I missing here?
EXECUTE AS impersonation is sandboxed to the current database unless the database is set to TRUSTWORTHY. Rather than turning on the TRUSTWORTHY database option, consider a less heavy-handed approach to extending security across databases. Methods to provide a non-privileged user permissions across databases include:
DB_CHAINING:
DB_CHAINING ON allows standard intra-database ownership chaining to extend across databases so that permissions on indirectly accessed objects are not required. Users need only execute permissions on your dbo.f stored procedure as long as the ownership chaining is unbroken. Note that users will still to be a user in database B but no object permissions need be granted. For example:
ALTER DATABASE A SET DB_CHAINING ON;
ALTER DATABASE B SET DB_CHAINING ON;
The implication with DB_CHAINING and dbo-owned objects is that both database A and B must be owned by the same login in order to maintain an unbroken ownership chain for the dbo user. The database owner(s) can be changed using ALTER AUTHORIZATION if needed:
ALTER AUTHORIZATION ON DATABASE::A to DatabaseOwnerLogin;
ALTER AUTHORIZATION ON DATABASE::B to DatabaseOwnerLogin;
Module Signing:
Module signing allows one to add additional permissions to a module via a certificate user. Create a certificate, create a user from the certificate, grant the certificate user the needed permissions, and then sign the stored procedure with the certificate. Below is sample code gleaned from this article.
An advantage of module signing over DB_CHAINING is that the calling user does not need to have be a user in database B because the cert user provides the security context. Be aware that the proc will need to be re-signed if it is later altered.
USE B;
-- create certifciate, cert user, and grant needed permissions
CREATE CERTIFICATE cross_database_cert
ENCRYPTION BY PASSWORD = 'All you need is love'
WITH SUBJECT = 'For cross-database access';
CREATE USER cross_database_cert FROM CERTIFICATE cross_database_cert;
GRANT SELECT ON dbo.SomeTable TO cross_database_cert;
GO
-- Copy cert to database A
DECLARE #cert_id int = cert_id('cross_database_cert')
DECLARE #public_key varbinary(MAX) = certencoded(#cert_id),
#private_key varbinary(MAX) =
certprivatekey(#cert_id,
'All you need is love',
'All you need is love')
SELECT #cert_id, #public_key, #private_key
DECLARE #sql nvarchar(MAX) =
'CREATE CERTIFICATE cross_database_cert
FROM BINARY = ' + convert(varchar(MAX), #public_key, 1) + '
WITH PRIVATE KEY (BINARY = ' +
convert(varchar(MAX), #private_key, 1) + ',
DECRYPTION BY PASSWORD = ''All you need is love'',
ENCRYPTION BY PASSWORD = ''All you need is love'')'
EXEC A.sys.sp_executesql #sql;
GO
ALTER CERTIFICATE cross_database_cert REMOVE PRIVATE KEY;
GO
USE A;
GO
--sign proc with certificate
ADD SIGNATURE TO dbo.f BY CERTIFICATE cross_database_cert
WITH PASSWORD = 'All you need is love';
GO
ALTER CERTIFICATE cross_database_cert REMOVE PRIVATE KEY;
GO

The server principal "sa" is not able to access the database "model" under the current security context

I created a stored procedure which performs a series of operations that require special permissions, e.g. create database, restore database, etc. I create this stored procedure with
execute as self
...so that it runs as SA. This is because I want to give a SQL user without any permissions the ability to run only these commands which I have defined.
But when I run this stored proc, I get
The server principal "sa" is not able to access the database "model" under the current security context.
How come SA can't access the model database? I actually ran the code in the stored proc on its own, under SA, and it ran fine.
Read Extending Database Impersonation by Using EXECUTE AS before you continue.
when impersonating a principal by using the EXECUTE AS USER statement,
or within a database-scoped module by using the EXECUTE AS clause, the
scope of impersonation is restricted to the database by default. This
means that references to objects outside the scope of the database
will return an error.
You need to use module signing. Here is an example.
For anybody else coming here looking for this info, here is sample code that demonstrates exactly what OP (and I) wanted (based on excellent info from Remus above):
-- how to sign a stored procedure so it can access other Databases on same server
-- adapted from this very helpful article
-- http://rusanu.com/2006/03/01/signing-an-activated-procedure/
USE [master]
GO
CREATE DATABASE TempDB1
CREATE DATABASE OtherDB2
GO
USE TempDB1
GO
-- create a user for TempDB1
CREATE USER [foo] WITHOUT LOGIN
GO
CREATE PROCEDURE TempDB1_SP
AS
BEGIN
CREATE TABLE OtherDB2.dbo.TestTable (ID int NULL)
IF ##ERROR=0 PRINT 'Successfully created table.'
END
GO
GRANT EXECUTE ON dbo.TempDB1_SP TO [foo]
GO
EXECUTE AS User='foo'
PRINT 'Try to run an SP that accesses another database:'
EXECUTE dbo.TempDB1_SP
GO
REVERT
-- Output: Msg 916, Level 14, State 1, Procedure TempDB1_SP, Line 5
-- [Batch Start Line 14]
-- The server principal "..." is not able to access the database
-- "OtherDB2" under the current security context.
PRINT ''
PRINT 'Fix: Try again with signed SP...'
-- Create cert with private key to sign the SP with.
-- Password not important, will drop private key
USE TempDB1
GO
-- create a self-signed cert
CREATE CERTIFICATE [DB_Cert]
ENCRYPTION BY PASSWORD = 'Password1'
WITH SUBJECT = 'Signing for cross-DB SPs';
-- Sign the procedure with the certificate’s private key
ADD SIGNATURE TO OBJECT::[TempDB1_SP]
BY CERTIFICATE [DB_Cert]
WITH PASSWORD = 'Password1'
-- Drop the private key. This way it cannot be used again to sign other procedures.
ALTER CERTIFICATE [DB_Cert] REMOVE PRIVATE KEY
-- Copy the public key part of the cert to [master] database
-- backup to a file and create cert from file in [master]
BACKUP CERTIFICATE [DB_Cert] TO FILE = 'C:\Users\Public\DBCert.cer'
USE [OtherDB2] -- or use [master] = all DBs on server accessible
GO
CREATE CERTIFICATE [DB_Cert] FROM FILE = 'C:\Users\Public\DBCert.cer';
-- the 'certificate user' carries the permissions that are automatically granted
-- when the signed SP accesses other Databases
CREATE USER [DB_CertUser] FROM CERTIFICATE [DB_Cert]
GRANT CREATE TABLE TO [DB_CertUser] -- or whatever other permissions are needed
GO
USE TempDB1
EXECUTE dbo.TempDB1_SP
GO
-- output: 'Successfully created table.'
-- clean up: everything except the cert file, have to delete that yourself sorry
USE [master]
GO
DROP DATABASE TempDB1
DROP DATABASE OtherDB2
GO

Determining the current security checks being made (SQL Server)

One thing that I've always hated more than just about anything in MS SQL Server is the way that security works. The security context constantly switches if you look at the server funny and it's often very hard (for me anyway) to predict or debug.
In dealing with an issue today, I though, "I wish I could just add a line to my code that would display the security context that SQL Server is using when this code runs." Does such a command exist? For example, SELECT security_context()
To be a little clearer... if I'm in a stored procedure and am therefor subject to the security context of the owner of the SP then I'd like to see that. If I'm in code that was called by sp_executesql and it's causing the security to be under the context of the SQL Server service account, then I would want to see that.
At least then I might be able to figure out why SQL Server thinks that I shouldn't have access to something.
Thanks!
EXAMPLE
-- Set up
CREATE USER Test_User WITHOUT LOGIN
CREATE TABLE Test_Security_Context (my_id INT)
INSERT INTO Test_Security_Context VALUES (1)
DENY SELECT ON Test_Security_Context TO Test_User
GO
CREATE PROCEDURE Test_Security_Context_SP
AS
SELECT SUSER_SNAME()
SELECT * FROM Test_Security_Context -- This will return ok
EXEC('SELECT SUSER_SNAME(); SELECT * FROM Test_Security_Context') -- SUSER_SNAME() will match above but select fails
GO
GRANT EXECUTE ON Test_Security_Context_SP TO Test_User
GO
-- Switch to the new user
SETUSER 'Test_User'
GO
-- Do the test
EXEC Test_Security_Context_SP
GO
-- Clean up
SETUSER
DROP PROCEDURE Test_Security_Context_SP
DROP TABLE Test_Security_Context
DROP USER Test_User
GO
Yes, there is such a pair of views that represents your current security context, considering all the details like EXECUTE AS or code signing:
sys.login_token for the server wide context
sys.user_token for the current database context
Every single access you get is ultimately derived from a row in the return of these results. Note that some access are implicit from hard coded role membership (like db_datareader database role or sysadmin server role).
Other that that:
ownership chaining is not related to security context: you are not under the 'context' of the SP owner. Ownership chaining simply states that access checks are skipped for objects owned by the same owner as current object (SP, View).
sp_executesql does not change the security context in any way
Not sure if this is what you mean by security context, but you can retrieve the user associated with your session like:
select SYSTEM_USER
This works for both a SQL Server login or a WIndows login. It even works inside stored procedures with execute as owner. For example,
create procedure dbo.Test
with execute as owner
as
select SYSTEM_USER
go
exec dbo.Test
select SYSTEM_USER
Prints:
sa
MyMachine\MyName
If you're looking for the Windows account that SQL Server is using to do things on your behalf, you could try to run whoami from the command like:
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE
EXEC master..xp_cmdshell 'whoami'
For me, that returns nt authority\network service.
I think you want to use CURRENT_USER to see the current security context. Here's an example:
SELECT CURRENT_USER AS 'Current User Name';
GO
EXECUTE AS LOGIN = 'junk'
GO
SELECT CURRENT_USER AS 'Current User Name';
GO
REVERT
SELECT CURRENT_USER AS 'Current User Name';
GO
with output (note: I'm admin on my SQL Server for this)
Current User Name
------------------
dbo
(1 row(s) affected)
Current User Name
------------------
Junk
(1 row(s) affected)
Current User Name
------------------
dbo
(1 row(s) affected)

Resources