SQL Server Permissions to Script Server Logins (But Not Alter Them) - sql-server

I have a script that tries to recreate part of a database. Part of that is to script out the logins that are used with that database.
I am trying to find a "cannot do harm" level of permission for this to run as.
Basically it needs to be able to see all the server logins to script them out (except passwords of course). But it needs to not have permissions to add, alter or delete anything on the server. Just script.
I looked at the roles and permissions for the server level, and I am not finding anything like that.
Does SQL Server have server level read only permissions for logins?

Does SQL Server have server level read only permissions for logins?
Yes, but it is not for ANY login, it is on a granular level, for each login:
https://learn.microsoft.com/en-us/sql/t-sql/statements/grant-server-principal-permissions-transact-sql
USE master;
GRANT VIEW DEFINITION ON LOGIN::EricKurjan TO RMeyyappan
GO
The downside is that you have to grant view definition for each login and every time a new login is created. For new logins, you could create a server DDL trigger which grants VIEW DEFINITION permission for the newly created login to your login (or better create a custom server role for this)
CREATE TRIGGER ddl_trig_for_create_login
ON ALL SERVER
FOR CREATE_LOGIN
AS
BEGIN
declare #newlogin sysname = quotename(EVENTDATA().value('(/EVENT_INSTANCE/ObjectName)[1]','sysname'));
declare #sql nvarchar(200) = 'GRANT VIEW DEFINITION ON LOGIN::'+ #newlogin +' TO public'; --<-- public just for testing.. change public to a custom server role
exec(#sql);
END
GO
Another option (not tested but logically it should work) would be to create a login from a certificate or asymmetric key, add the cert/asym login to the securityadmin role and then sign your script (the one which reads logins) with the cert/asym key.

Related

sql server 2008 r2 - deny create and drop database but allow everything else

I want to create a new user 'user1' that will be able to see all databases and all tables and also execute SQL statement, procedures etc.
The only thing that I want to deny is creating/dropping or change database (rename, change properties, create new database or drop existing database).
How can I do it?
If it is a dev server, I find it easiest to create a role in MODEL database that has all the right mix of privileges and then assign that role to a user mapped to a login that you need. When you create a new database that user will automatically be assigned privileges to that database.
Two caveats here: that login must be present on the server or else you will not be able to create a new database. Another important one is that the user will have completely unnecessary privileges to read and write to model database by default - definitely NOT a good idea on a production server. On a production server just create the role in model database, but only create user for the databases that you need.
This is the script you can use to create the role:
USE [model]
GO
CREATE ROLE [db_data_read_write_execute] AUTHORIZATION [db_securityadmin]
GO
ALTER ROLE [db_datareader] ADD MEMBER [db_data_read_write_execute]
GRANT EXECUTE to [db_data_read_write_execute]
GO
CREATE USER [DML_Only_User] FOR LOGIN [DML_Only_User_Login] WITH DEFAULT_SCHEMA=[dbo]
GO
ALTER ROLE [db_data_read_write_execute] ADD MEMBER [DML_Only_User]

How to give permission to rename a database sql-server

I have a user who needs to rename a database. I could give dbcreator privileges, but this would allow the user to rename any database, and even create new ones.
So I tried to create a stored procedure that the user would call to do the job.
CREATE PROCEDURE SPMyRenameDB
WITH EXECUTE AS 'MySuperUser' -- MySuperUser is a SQL user with dbcreator permission
AS
ALTER DATABASE A MODIFY NAME = B
GO
I get an error :
The server principal "MySuperUser" is not able to access the database "A" under the current security context.
I tried with sp_renamedb, I get : User does not have permission to perform this action.
Even a simple SELECT statement to a table in database A is not allowed : The server principal "MySuperUser" is not able to access the database "A" under the current security context.
When I connect as MySuperUser and query the database A, it works as expected. (MySuperUser is a SQL user with dbCreator and sysAdmin privileges on the server).
I suspect that the "WITH EXECUTE AS" statement has some security restrictions that do not allow to use it outside of the current database.
The Stored Procedure is in a database (other than A and B) where the user has db_owner permissions.
Any suggestions ? I do not need to stick with my "WITH EXECUTE AS" approach. Anything that would do the trick is welcome.
Thanks,
Yves
Check ALTER DATABASE in MSDN -> Permissions
Requires ALTER permission on the database.
So just query as following
USE A
GO
GRANT ALTER TO 'someuser'
GO
User must be member of dbcreator server role. (MSDN documentation is wrong!).

Is it possible to get "WITH EXECUTE AS " working under "dbo user/Administrator login" permissions (VIEW SERVER STATE needed)?

The problem can be emulated this way:
DECLARE #C1 BIGINT,#C2 BIGINT;
PRINT SUSER_NAME()+ ' '+ USER_NAME();
SELECT #C1=transaction_id from sys.dm_tran_current_transaction (nolock)
PRINT #C1
EXECUTE AS USER = 'dbo';
PRINT SUSER_NAME()+ ' '+ USER_NAME();
SELECT #C2=transaction_id from sys.dm_tran_current_transaction (nolock)
PRINT #C2
REVERT;
Called from 'Administrator' login (db owner, linked to dbo user) returns:
Administrator dbo
2209599
Administrator dbo
Msg 297, Level 16, State 1, Line 7
The user does not have permission to perform this action.
The same user but different results. Why?
Details:
I have a SP that contains select from sys.dm_os_sys_info and therefore this SP's caller should have VIEW SERVER STATE permissions.
I have modified SP header with "WITH EXECUTE AS OWNER". Owner is standard 'dbo' database user linked to 'Administrator' server login. Administrator is a member of sysadmin and have effective permission 'VIEW SERVER STATE', but execution of my modified procedure brings the 'The user does not have permission to perform this action' error... I see there logic since DBO database user by itself doesn't have server permissions (even if Administrator login has). But what next?
I have tried:
GRANT VIEW SERVER STATE TO DBO - doesn't work because of 'dbo is
not a user-defined server role' error.
Create new user 'ServerStateViewer' linked to 'Administrator' - doesn't work becuase of 'the login already has an account under a different user name' (as I understand it - dbo user exits in the dabatabse)
Create new user 'ServerStateViewer' not linked to any login - doesn't work because I can't to add any server permissions to this user.
WITH EXECUTE AS 'Administrator' - doesn't work because of 'Administrator is not a database user' error.
So it seems I'm forced to create new server login. I am interesting, may be there are still other ways to get WITH EXECUTE AS working under dbo user / Administrator login permissions?
You'll need to use code signing. This is not a problem, since code signing is what you should be using to start with to grant permission to procedures... The proper sequence is this:
inspect the procedure code to ensure that you trust it
change the procedure to have an EXECUTE AS OWNER clause
create a certificate with a private key in the SP's database
sign the procedure with the private key of the certificate you created
drop the private key of the certificate (to prevent it from ever being used again)
copy the certificate into the master database
create a login from the certificate
grant AUTHENTICATE SERVER to the certificate derived login
grant any additional priviledge required by the procedure (e.g. VIEW SERVER STATE) to the certificate derived login
For an example see Signing an activate procedure.
Note that during development code signing can be a pain because you will have to re-sign the procedure after every change to it. For dev purposes, do not drop the private key right after signing, so it can be reused.

How do I create a new user in a SQL Azure database?

I am trying to use the following template:
-- =================================================
-- Create User as DBO template for SQL Azure Database
-- =================================================
-- For login <login_name, sysname, login_name>, create a user in the database
CREATE USER <user_name, sysname, user_name>
FOR LOGIN <login_name, sysname, login_name>
WITH DEFAULT_SCHEMA = <default_schema, sysname, dbo>
GO
-- Add user to the database owner role
EXEC sp_addrolemember N'db_owner', N'<user_name, sysname, user_name>'
GO
I would like to create a user called user1 with a password of 'user1pass'. I connected with my default database 'authentication' and I have a query window open.
But the template does not make sense for me. For example what's sysname, where do I supply the password and what should I use as the default_schema?
The particular user needs to have the power to do everything. But how do I set it up so he can do everything, is that done if I make the user a database owner?
So far I have tried:
CREATE USER user1, sysname, user1
FOR LOGIN user1, sysname, user1
WITH DEFAULT_SCHEMA = dbo, sysname, dbo
GO
Giving:
Msg 102, Level 15, State 1, Line 1 Incorrect syntax near ','.
and:
CREATE USER user1
FOR LOGIN user1
WITH DEFAULT_SCHEMA = dbo
GO
Giving:
Msg 15007, Level 16, State 1, Line 1 'user1' i
s not a valid login or you do not have permission.
Edit - Contained User (v12 and later)
As of Sql Azure 12, databases will be created as Contained Databases which will allow users to be created directly in your database, without the need for a server login via master.
Sql (standard) User
CREATE USER [MyUser] WITH PASSWORD = 'Secret';
ALTER ROLE [db_datareader] ADD MEMBER [MyUser]; -- or sp_addrolemember
AAD linked User
CREATE USER [SomeUser#mydomain.com] FROM EXTERNAL PROVIDER;
EXEC sp_addrolemember 'db_datareader' , N'SomeUser#mydomain.com'
AAD linked Group
CREATE USER [SomeGroup] FROM EXTERNAL PROVIDER;
EXEC sp_addrolemember 'db_datareader' , N'SomeGroup'
NB! when connecting to the database when using a contained user that you must always specify the database in the connection string.
Traditional Server Login - Database User (Pre v 12)
Just to add to #Igorek's answer, you can do the following in Sql Server Management Studio:
Create the new Login on the server
In master (via the Available databases drop down in SSMS - this is because USE master doesn't work in Azure):
create the login:
CREATE LOGIN username WITH password=N'password';
Create the new User in the database
Switch to the actual database (again via the available databases drop down, or a new connection)
CREATE USER username FROM LOGIN username;
(I've assumed that you want the user and logins to tie up as username, but change if this isn't the case.)
Now add the user to the relevant security roles
EXEC sp_addrolemember N'db_owner', N'username'
GO
(Obviously an app user should have less privileges than dbo.)
Check out this link for all of the information : https://azure.microsoft.com/en-us/blog/adding-users-to-your-sql-azure-database/
First you need to create a login for SQL Azure, its syntax is as follows:
CREATE LOGIN username WITH password='password';
This command needs to run in master db. Only afterwards can you run commands to create a user in the database. The way SQL Azure or SQL Server works is that there is a login created first at the server level and then it is mapped to a user in every database.
HTH
I followed the answers here but when I tried to connect with my new user, I got an error message stating "The server principal 'newuser' is not able to access the database 'master' under the current security context".
I had to also create a new user in the master table to successfully log in with SSMS.
USE [master]
GO
CREATE LOGIN [newuser] WITH PASSWORD=N'blahpw'
GO
CREATE USER [newuser] FOR LOGIN [newuser] WITH DEFAULT_SCHEMA=[dbo]
GO
USE [MyDatabase]
CREATE USER newuser FOR LOGIN newuser WITH DEFAULT_SCHEMA = dbo
GO
EXEC sp_addrolemember N'db_owner', N'newuser'
GO
You can simply create a contained user in SQL DB V12.
Create user containeduser with password = 'Password'
Contained user login is more efficient than login to the database using the login created by master. You can find more details # http://www.sqlindepth.com/contained-users-in-sql-azure-db-v12/
I use the Azure Management console tool of CodePlex, with a very useful GUI, try it. You can save type some code.
1 Create login while connecting to the master db
(in your databaseclient open a connection to the master db)
CREATE LOGIN 'testUserLogin' WITH password='1231!#ASDF!a';
2 Create a user while connecting to your db (in your db client open a connection to your database)
CREATE USER testUserLoginFROM LOGIN testUserLogin;
Please, note, user name is the same as login. It did not work for me when I had a different username and login.
3 Add required permissions
EXEC sp_addrolemember db_datawriter, 'testUser';
You may want to add 'db_datareader' as well.
list of the roles:
https://learn.microsoft.com/en-us/sql/relational-databases/security/authentication-access/database-level-roles?view=sql-server-ver15
I was inspired by #nthpixel answer, but it did not work for my db client DBeaver.
It did not allow me to run USE [master] and use [my-db] statements.
https://azure.microsoft.com/en-us/blog/adding-users-to-your-sql-azure-database/
How to test your user?
Run the query bellow in the master database connection.
SELECT A.name as userName, B.name as login, B.Type_desc, default_database_name, B.*
FROM sys.sysusers A
FULL OUTER JOIN sys.sql_logins B
ON A.sid = B.sid
WHERE islogin = 1 and A.sid is not null
List of all users in Azure SQL
create a user and then add user to a specific role:
CREATE USER [test] WITH PASSWORD=N'<strong password>'
go
ALTER ROLE [db_datareader] ADD MEMBER [test]
go
I found this link very helpful:
https://azure.microsoft.com/en-gb/documentation/articles/sql-database-manage-logins/
It details things like:
- Azure SQL Database subscriber account
- Using Azure Active Directory users to access the database
- Server-level principal accounts (unrestricted access)
- Adding users to the dbmanager database role
I used this and Stuart's answer to do the following:
On the master database (see link as to who has permissions on this):
CREATE LOGIN [MyAdmin] with password='ReallySecurePassword'
And then on the database in question:
CREATE USER [MyAdmin] FROM LOGIN [MyAdmin]
ALTER ROLE db_owner ADD MEMBER [MyAdmin]
You can also create users like this, according to the link:
CREATE USER [mike#contoso.com] FROM EXTERNAL PROVIDER;
I think the templates use the following notation: variable name, variable type, default value.
Sysname is a built-in data type which can hold the names of system objects.
It is limited to 128 Unicode character.
-- same as sysname type
declare #my_sysname nvarchar(128);

Current transaction ID in an audit trigger

I was looking at storing some form of transaction id from an audit trigger. The solution appeared to be to use sys.dm_tran_current_transaction as in this post SQL Server Triggers - grouping by transactions.
However, I cannot use this because the user account running sql statements will not have the "VIEW SERVER STATE" permission and results in the error:
Msg 297, Level 16, State 1, Line 3
The user does not have permission to perform this action.
Does anyone know of an alternative to this view that will provide a similar transaction id or a way to use "WITH EXECUTE AS" on the trigger to allow selecting from this view.
From my attempts at "WITH EXECUTE AS" it appears that server level permissions are not carried over, which is expected really since it is impersonating a database user.
You can resolve almost any security problem using code signing. Most granular and finely tuned access control, is just a bit on the hard side to understand.
Use EXECUTE AS OWNER on the trigger, create a certificate, sign the trigger, drop the private key (so that noone else can use it to ever sign anything again), export the certificate (public key only), import the certificate in master, create a login derived from the certificate, grant authenticate to this login (in order to extend the database execute as impersonation), then grant view server state to this login. This is bullet proof, perfectly controled priviledge control. If the trigger need to be changed, the signing process (including the cert derived login and grants) have to be done again. From a security point of view, this is desired (you are signing a specific variant of the trigger), from operational point of view is rather a pita, but is manageable.
create table t (i int);
create table audit (transaction_id int);
go
create trigger t_audit_trigger
on t
with execute as owner
after insert, update, delete
as
begin
set nocount on;
insert into audit (transaction_id)
select transaction_id from sys.dm_tran_current_transaction;
if (##ROWCOUNT != 1)
raiserror(N'Failed to audit transaction', 16, 1);
end
go
create certificate t_audit_view_server
encryption by password = 'Password#123'
with subject = N't_audit_view_server'
, start_date = '08/10/2009';
go
add signature to t_audit_trigger
by certificate t_audit_view_server
with password = 'Password#123';
go
alter certificate t_audit_view_server
remove private key;
backup certificate t_audit_view_server
to file = 'c:\temp\t_audit_view_server.cer';
go
use master;
go
create certificate t_audit_view_server
from file = 'c:\temp\t_audit_view_server.cer';
go
create login t_audit_view_server_login
from certificate t_audit_view_server;
go
grant authenticate server to t_audit_view_server_login;
grant view server state to t_audit_view_server_login;
go
From SQL Server 2008, Microsoft introduced sys.dm_exec_requests, which is to deprecate sys.sysprocesses. This view returns the transaction_id, and can be called without granting VIEW SERVER STATE. Like sys.sysprocesses, it returns details for the current process if VIEW SERVER STATE is not granted, and all processes if it is.
Although not directly answering your question, rather than using a custom built auditing framework, in SQL Server 2008 you could make use of the Change Data Capture Technology.
See the following reference from Books Online: http://msdn.microsoft.com/en-us/library/bb522489.aspx
EDIT (Solution, added): Here is a walkthrough of how to create a stored procedure to access the system view, making use of the execute as clause and using impersonation.
USE MASTER;
select * from sys.dm_tran_current_transaction
--Create a login with view server state permissions
CREATE LOGIN ViewServerStateLogin
WITH password = 'Hello123';
CREATE user ViewServerStateLogin;
--Create a login to test the permissions assignment
CREATE LOGIN TestViewServerState
WITH password = 'Hello123';
CREATE user TestViewServerState;
--Test with Login
EXECUTE AS LOGIN = 'TestViewServerState';
--This obviously does not work.
select * from sys.dm_tran_current_transaction
revert;
--Grant view server state permission to the ViewServerStateLogin
GRANT VIEW SERVER state TO ViewServerStateLogin;
--Create a procedure to wrap the call to the system view
CREATE PROCEDURE proc_TestViewServerState
AS
SET NOCOUNT ON;
EXECUTE AS LOGIN='ViewServerStateLogin'
select * from sys.dm_tran_current_transaction
revert;
RETURN(0);
--Assign execute permission to the test accounts
GRANT EXECUTE ON proc_TestViewServerState TO TestViewServerState
--Grant impersonation rights to the test login
GRANT IMPERSONATE ON LOGIN::ViewServerStateLogin TO TestViewServerState
--Test with Procedure
EXECUTE AS LOGIN = 'TestViewServerState';
EXEC proc_TestViewServerState
revert;
Starting with SQL Server 2016, you could use CURRENT_TRANSACTION_ID. According to the docs:
Any user can return the transaction ID of the current session.
Encrypt the stored procedure and don't share the pw for ViewServerStateLogin. Then you get a black box of sufficient density to satisfy the auditors.

Resources