We have a need where a user with low permissions can kick off 1 job, through a proc. So, we:
My question is why will this not work? We use something similar to allow a user to restore a database on one of our servers. The only way to get this to work for me right now is to give the low permissions user higher permissions in msdb db. This obviously isn't ideal and undoes exactly what we're trying to avoid.
The sample permissions code is below and then the proc code is below that.
USE [master];
GO
CREATE CERTIFICATE [TestCert]
ENCRYPTION BY PASSWORD = N'ComplicatedPassword'
WITH SUBJECT = N'Certificate to allow user to kick off one job';
GO
ADD SIGNATURE
TO [dbo].[ProcThatCallsJob]
BY CERTIFICATE [TestCert]
WITH PASSWORD = N'ComplicatedPassword';
GO
CREATE LOGIN [TestLoginFromCert] FROM CERTIFICATE [TestCert]
GO
ALTER SERVER ROLE [sysadmin] ADD MEMBER [TestLoginFromCert]
GO
CREATE ROLE [RoleToExecuteProc]
GO
GRANT EXECUTE ON OBJECT::[dbo].[ProcThatCallsJob]
TO [RoleToExecuteProc];
GO
CREATE USER [TestUser_LowPermissions]
FOR LOGIN [TestUser]
GO
ALTER ROLE [RoleToExecuteProc] ADD MEMBER [TestUser_LowPermissions]
GO
USE [msdb]
GO
CREATE USER [TestUserFromCert]
FOR LOGIN [TestLoginFromCert]
GO
ALTER ROLE [db_datareader] ADD MEMBER [BDWReportUserRunJobs]
GO
ALTER ROLE [SQLAgentOperatorRole] ADD MEMBER [BDWReportUserRunJobs]
GO
The proc, [dbo].[ProcThatCallsJob]:
USE [master]
GO
CREATE PROCEDURE [dbo].[ProcThatCallsJob]
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
DECLARE @sql NVARCHAR(MAX);
SET @sql = N'USE msdb
EXEC msdb.dbo.sp_start_job @job_name = ''10171_TestJob''
'
EXECUTE sp_executesql @sql;
END
Is the issue in any way related to the fact that the proc is signed with a certificate in master db (associated with a login with sysadmin privileges)? I can put the certificate in msdb db, but I wouldn't have anything to sign it to there.
EDIT - I created the proc and certificate in msdb db as a test of my hypothesis that change databases was causing some concern. No luck. The [TestUser_LowPermissions] still gets an error that they cannot execute sp_start_job. So, I'm still at a loss as to why the signed proc is not inheriting the [TestLoginFromCert] permissions of sysadmin.
From MSDN forum:
Everything you did so far was correct in itself. The problem lies within the nested system stored procedures themselves.
The thing is: sp_start_job is doing another permission/role check, hard-coded:
-- Check permissions beyond what's checked by the sysjobs_view
-- SQLAgentReader role can see all jobs but
-- cannot start/stop jobs they do not own
IF (@job_owner_sid <> SUSER_SID() -- does not own the job
AND (ISNULL(IS_SRVROLEMEMBER(N'sysadmin'), 0) = 0) -- is not sysadmin
AND (ISNULL(IS_MEMBER(N'SQLAgentOperatorRole'), 0) = 0)) -- is not SQLAgentOperatorRole
BEGIN
RAISERROR(14393, -1, -1);
RETURN(1) -- Failure
END
Since you don’t want to mess with system code, there are not many options left: 1. Work with Impersonation to sa: highly risky 2. Use a Trustworthy database – I personally dislike openly saying that a lot, because it is hard enough to educate the average users NOT to use this concept. But you can secure this if it is your very own code-only database 3. Use a combination of [SQLAgentOperatorRole] and your own procedure. Meaning: Make the user member of that role and then deny execute on sp_startjob (and other procs). This way you can grant the execute via your own proc and circumvent the hard-coded check by giving him the [SQLAgentOperatorRole].