I'm using SQL Server 2014.
I have granted EXECUTE on a stored procedure to a user account, but when the sp is run, I get both a result and an error. This is from SSMS, with "Results to Text" selected:
set nocount on
use trx_d
go
select specific_name
from information_schema.routines
where routine_type = 'PROCEDURE'
and specific_name = 'proc_plan_get_count';
go
exec dbo.proc_plan_get_count 225, 2016;
go
specific_name
----------------------------------
proc_plan_get_count
status_code status_count
-------------------- ------------
Approved 1
Msg 15151, Level 16, State 1, Procedure proc_plan_get_count, Line 25
Cannot find the object 'proc_plan_get_count', because it does not exist or you do not have permission.
I tried dropping and recreating the sp (which just contains a SELECT statement with a WHERE clause reflecting the parameters being passed in). I thought that might clear the cobwebs, since when I tried to create a new, simple procedure I had no problems. The drop & create had no effect, with the same behavior above appearing.
I changed the contents of the sp to avoid any table references, ran it and I received no error. Putting back in the SELECT stmt caused the issue to re-surface. This user account is in the db_datareader role, though (and you can see in the output above that it is successfully reading the table).
Then I tried just recreating the procedure under a new name (I added a "2" at the end), and when running it I received no errors.
I am thoroughly confused, thanks for any help on figuring this out,
--=Chuck
Being new to SQL Server, this one escaped me for a bit. Here's the gist of the code when I request to modify it through SSMS:
USE [trx_d]
GO
/****** Object: StoredProcedure [dbo].[proc_plan_get_count] Script Date: 1/25/2016 12:36:49 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[proc_plan_get_count]
-- Add the parameters for the stored procedure here
#EmployeeId int = 0,
#PlanYear int = 0
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT stdcode as status_code, COUNT(*) as status_count
FROM **********
WHERE yr = #PlanYear AND
empnum = #EmployeeId
GROUP BY stdcode
END
GRANT EXEC ON [dbo].proc_plan_get_count TO *****
So, since there's no GO after the END keyword, that GRANT stmt is actually part of the source code, and was the source of the error upon sp execution.
Why did we hit it now? We were switching to a new database login, and while the new login looks to have all of the same privileges as the old login, there must be some other privilege that the old login has which doesn't balk at the GRANT EXEC step. It throws a warning instead of an error:
status_code status_count
-------------------- ------------
Approved 3
Cannot grant, deny, or revoke permissions to sa, dbo, entity owner, information_schema, sys, or yourself.
Related
I'm trying to run a stored procedure which starts up a job. When I run it as myself (as a sysadmin), it runs without a hitch, but when I run it as a custom SQL Server user ("SQLAgentUser" -- mapped to a SQL Server login), it gives the error:
EXECUTE permission was denied on the object 'sp_start_job', database 'msdb', schema 'dbo'.
SQLAgentUser is a member of all the following roles in msdb:
SQLAgentOperatorRole
SQLAgentReaderRole
SQLAgentUserRole
In addition, SQLAgentUser has the following explicit permissions granted in MSDB:
GRANT EXECUTE on sp_start_job (and when I look at effective permissions in SSMS, it says it has Execute permissions)
GRANT EXECUTE on sp_stop_job (same as above)
In MyDb (not its actual name), SQLAgentUser has EXECUTE permissions to the sqladm schema, as well as DELETE, INSERT, SELECT, and UPDATE permissions to sqladm.AgentJobsLastRun.
According to everything I found online, this SHOULD be all that's needed, but I'm still getting the error when executing as SQLAgentUser.
Here's the erroring code:
USE [MyDb]
GO
DECLARE #RC int
EXECUTE AS USER = 'SQLAgentUser'
UPDATE [sqladm].[AgentJobsLastRun]
SET RunDate = NULL
WHERE JobName = 'MonthlyJobs'
EXECUTE #RC = [sqladm].[udp_DailyJob]
REVERT
GO
If I comment out, "EXECUTE AS USER = 'SQLAgentUser'," it runs without a hitch.
...and the code inside [sqladm].[udp_DailyJob]:
SET NOCOUNT ON;
-- Insert statements for procedure here
-- First check that the monthly job has run this month (should have run at 2 AM on the first). If not, manually run the job.
DECLARE #AgentJobNameSys nvarchar(128) = N'MonthlyJobs'
DECLARE #LastRunDate datetime = COALESCE((SELECT [RunDate] FROM [sqladm].[AgentJobsLastRun] WHERE JobName = #AgentJobNameSys),DATEADD(month,-1,getdate()))
IF #LastRunDate < DATEFROMPARTS(YEAR(getdate()),MONTH(getdate()),1)
BEGIN
EXEC msdb.dbo.sp_start_job #AgentJobNameSys;
END
Running SQL Server 2019 Developer Edition on my local computer.
Many thanks! :)
I get an error from one of my databases when trying to execute this one
create or alter procedure [dbo].[test_sp]
with execute as owner
as
SELECT SUSER_SNAME()+ ' '+ USER_NAME();
begin
exec master..sp_trace_generateevent #eventid = 82 ,
#userinfo=N'test'
end
GO
exec [dbo].[test_sp]
Error:
Msg 8189, Level 14, State 10, Procedure master..sp_trace_generateevent, Line 1 [Batch Start Line 9]
You do not have permission to run 'SP_TRACE_GENERATEEVENT'.
Granted ALTER TRACE to my user (which returns in SUSER_SNAME()), but it wasn't help
The same script on the second database (same server) works without errors.
What else can it be?
You're trying to run this with EXECUTE AS OWNER, and the owner is a database-level principal and you can't operate outside the current database while impersonating a database-level principal. Switch to EXECUTE AS CALLER (the default) to have the caller's identity used to run the proc in master. eg
create or alter procedure [dbo].[test_sp]
with execute as caller
as
SELECT SUSER_SNAME()+ ' '+ USER_NAME();
begin
exec master..sp_trace_generateevent #eventid = 82, #userinfo = N'test'
end
GO
exec [dbo].[test_sp]
This can be made to work with owner-impersonation by marking the database as TRUSTWORTHY. See: Extending Database Impersonation by Using EXECUTE AS and Guidelines for using the TRUSTWORTHY database setting in SQL Server
I'm working with SQL Server 2012 Express.
I'm using Service Broker to run a stored procedure asynchronously.
The activation procedure has to access another database to execute another stored procedure. This is the code:
CREATE PROCEDURE [dbo].[GetNewCodes]
#gintNewCodes bigint,
#presNewCodes tinyint,
#levelNewCodes bigint,
#quantityNewCodes smallint
AS
-- Get new codes from INCIC database.
DECLARE #return_value int,
#xmlGenerated xml,
#xmlString NVARCHAR(MAX)
SET NOCOUNT ON;
-- Set that this stored procedure is running
update dbo.RunningSPs with (serializable) set conf_value = 1
where sp_name = N'GetNewCodes'
if ##rowcount = 0
begin
insert dbo.RunningSPs(sp_name, conf_value) values (N'GetNewCodes', 1)
end
EXEC #return_value = [INCIC].[dbo].[ReadCodeBuffer]
#gint = #gintNewCodes,
#pres = #presNewCodes,
#level = #levelNewCodes,
#quantity = #quantityNewCodes,
#xmlGenerated = #xmlGenerated OUTPUT
SET #xmlString = cast(#xmlGenerated as nvarchar(max))
-- Process these new codes on TRZ.
EXEC dbo.ProcessCodes #XmlString = #xmlString
-- Update that we are not running this procedure any more.
update dbo.RunningSPs with (serializable) set conf_value = 0
where sp_name = N'GetNewCodes'
if ##rowcount = 0
begin
insert dbo.RunningSPs(sp_name, conf_value) values (N'GetNewCodes', 0)
end
The problem is here: [INCIC].[dbo].[ReadCodeBuffer], and the error message is:
Error: 50000
Unrecoverable error in procedure GetNewCodes: 916: The server principal "sa" is not able to access the database "INCIC" under the current security context.
I have followed this tutorial to implement Service, queue and activation stored procedure.
How can I fix this problem?
Read Call a procedure in another database from an activated procedure.
The problem is that activated procedures are run under an EXECUTE AS USER context and as such are subject to database impersonation restrictions (they are sandboxed within the database). Is all explained in Extending Database Impersonation by Using EXECUTE AS.
The solution is to sign your activated procedure and create an user derived from the signing certificate in the target database, and grant this derived user the required permissions. The first link shows a full example.
Remus Rusanu's answer is clearly definitive and correct. (Anyone dealing with Broker Services knows his expertise and invaluable blog.)
I just wanted to document my experience, since google will direct to this question when searching on "The server principal "sa" is not able to access the database..."
In my case, I was call another database from within an activated procedure, but invoking a sql statement directly, not a stored procedure.
Originally, the cross-database call worked fine without a signed certificate and the use of impersonation. Then, after a small syntax change, it started returning the above error message.
Here is what worked without the need for a signed certificate:
IF EXISTS (SELECT name FROM sys.databases WHERE name = N'MyOtherDb')
and here is what provoked the security exception:
IF DB_ID(N'MyOtherDb') IS NOT NULL
I have a user who has db_datareader, db_datawriter permissions on a DB.
I want to set the isolation levels to the DB to which the user has access to.
What permissions will my user need to be able to set these.
DB used: SQL SERVER 2008
This is not setting an isolation level:
ALTER DATABASE dbname SET ALLOW_SNAPSHOT_ISOLATION ON;
That is altering the database. For that you need to provide them with ALTER rights on the database:
GRANT ALTER ON DATABASE::dbname TO username;
Otherwise you get this error:
Msg 5011, Level 14, State 9, Line 1
User does not have permission to alter database 'dbname', the database does not exist, or the database is not in a state that allows access checks.
Msg 5069, Level 16, State 1, Line 1
ALTER DATABASE statement failed.
Now, ALTER is all or nothing - you can't use it to allow them to change the allow snapshot setting but not other settings like forced parameterization, compatibility level, etc. You can do this much more granularly, though; perhaps you could create a stored procedure that does something like this:
CREATE PROCEDURE dbo.SetIsolationLevel
WITH EXECUTE AS OWNER
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX) = N'ALTER DATABASE '
+ QUOTENAME(DB_NAME())
+ ' SET ALLOW_SNAPSHOT_ISOLATION ON;';
EXEC sp_executesql #sql;
END
GO
Now you just have to give the user EXEC permissions on that procedure, which your user can now call (instead of the ALTER DATABASE command explicitly and instead of giving them full ALTER DATABASE privileges):
GRANT EXEC ON dbo.SetIsolationLevel TO username;
GO
You can simulate them calling this stored procedure by logging in as them, or using the EXECUTE AS feature directly:
EXECUTE AS USER = 'username';
GO
EXEC dbo.SetIsolationLevel;
GO
REVERT;
GO
Another idea is to simply set the model database to have this setting, than any new databases that get created for your users will automatically inherit it, then you don't have to worry about making them turn it on.
ALTER DATABASE model SET ALLOW_SNAPSHOT_ISOLATION ON;
GO
CREATE DATABASE splunge;
GO
SELECT snapshot_isolation_state_desc FROM sys.databases WHERE name = N'splunge';
Result:
ON
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)