I need to set up a system which will allow developers to request an emergency ID for a database. They will be assigned to a role called ‘analyst’ which will provide them a drop down box with the databases they can gain access to. They will submit the request and a temporary SQL Login will be generated and displayed on screen. The login will have some elevated privs. The login will be removed after 12 hours.
I’ve got the whole thing working myself as an SA on ASP.net, but now I’m working on modifying the procedures to work using a SQL Login in the application connection string.
I’ve tried a few things to get it working, but have run into a roadblock.
Here’s my procedure that does the real work.
USE [SQLEmergencyLoginRequest]
GO
/****** Object: StoredProcedure [dbo].[SQLELR_Login_CREATE] Script Date: 12/08/2009 14:48:29 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[SQLELR_Login_CREATE]
@SERVER VARCHAR(50),
@DATABASE VARCHAR(50),
@NTLOGIN VARCHAR(50),
@IR INT,
@LOGIN VARCHAR(50) OUTPUT,
@PWD VARCHAR(20) OUTPUT,
@NotifyDBA INT
WITH EXECUTE AS OWNER
AS
/*
Emergency_Access_Login_CREATE: Create Login/PWD, Create User, Create Role, Add User to Role, return Login/PWD.
*/
DECLARE @Random_Login_Extension VARCHAR(20)
DECLARE @sql VARCHAR(1000)
SET @Database = QUOTENAME(@Database);
BEGIN TRANSACTION
--CREATE LOGIN/PWD
EXEC dbo.random_password @Random_Login_Extension OUTPUT;
EXEC dbo.random_password @PWD OUTPUT;
SET @LOGIN = 'Emergency_Login_' + @Random_Login_Extension;
SET @sql= 'CREATE LOGIN [' + @LOGIN + ']' +
'WITH PASSWORD= ''' + @PWD + ''', DEFAULT_DATABASE=[master], ' +
'CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF';
EXEC(@sql);
--CREATE USER
DECLARE @User_Cmd VARCHAR(1000);
SET @User_Cmd = 'USE ' + @DATABASE + ';' +
'CREATE USER [' + @LOGIN + '] FOR LOGIN [' + @LOGIN + '];' +
'EXEC sp_addrolemember N''db_datareader'',''' + @LOGIN + ''';' +
'EXEC sp_addrolemember N''db_datawriter'',''' + @LOGIN + ''';' +
'EXEC sp_addrolemember N''db_ddladmin'',''' + @LOGIN + ''';';
EXEC (@User_Cmd);
INSERT INTO dbo.SQLELR_Emergency_Logins
([CreationTime]
,[NTLogin]
,[IR]
,[SERVER]
,[DATABASE]
,SQLLoginCreated)
VALUES
(GETDATE()
,@NTLOGIN
,@IR
,@SERVER
,@DATABASE
,@LOGIN)
DECLARE @MYBODY VARCHAR(500)
SET @MYBODY = @NTLOGIN + ' has created a temporary login in the ' + @Database + ' Database. The login name is ' + @LOGIN;
DECLARE @MYSUBJECT VARCHAR(500)
SET @MYSUBJECT = 'Emergency Login Creation ON server ' + @@SERVERNAME;
IF @NotifyDBA = 1
BEGIN
EXEC msdb.dbo.sp_notify_operator
@profile_name = 'SQLDBA',
@name = 'SQLDBA',
@subject = @MYSUBJECT,
@body = @MYBODY;
END
COMMIT TRANSACTION
I don’t want the application account to be highly privileged in every DB, so I created another account which will go into every db and have db_owner. Evidently the sp_addrolemember using fixed db roles needs a db_owner to work, which is why the acct is db_owner. I’d prefer security admin, but it seems it’s not possible.
Back to the problem – using EXECUTE AS with dynamic code does not work.
Is the only way to get this done by creating a stored procedure in every database which creates the user?
We’re doing this because we’d like to crank down security on this server and take away db_owner from developers which has been the norm for years. Creating this mechanism will satisfy their only remaining complaint about not having access. They are afraid we won’t answer a page and they will be unable to resolve an issue, so this will take care of that.
Of course, any advice on security holes here would be appreciated as well.
The EXECUTE AS clause on the work procedure puts you into the ‘execute as’ cage, see Extending Database Impersonation by Using EXECUTE AS. Because the EXECUTE AS of the procedure is an database principal, the execute as context will be trusted only inside the database.
There are two workarounds, the 500lb sledge hammer of
ALTER DATABASE [SQLEmergencyLoginRequest] SET TRUSTWORTHY ONor the surgical precission tool of code signing, see Call a procedure in another database from an activated procedure for an example. I highly recommend the code signing approach:This would ensure that the procedure has all the needed priviledges to do its work, in any database. You have to redo the whole signing procedure every time you alter it.