Archive for December, 2008

SQL Server Security – Database Roles

Wednesday, December 31st, 2008

Database roles (also referred to as database-level roles) are security roles that exist on a database level, as opposed to the server level. If you are familiar with any aspect of system administration, database roles are similar to groups in the world of Windows system administration. Just like a Windows group, when a user is added to a role they inherit all the rights and permissions of the role.

There are two kinds of database roles in SQL Server: fixed roles and flexible roles.

Fixed Roles

Before getting into the fixed roles that are available, it’s important to know what fixed roles actually are. Fixed roles are roles that automatically exist in each database. Adding a user to one of these roles will not change that user’s permissions in any other database.

Any user or role can be added to a database role. Once a user has been added to a role, they can add other users or roles to that role.

A Word of Warning: Be exceptionally carefully when adding flexible roles to a fixed role. You could very easily elevate privileges for a large number of users in one simple step.

With that warning out of the way, let’s take a look at the fixed roles available in SQL Server.

  • db_owner
  • db_securityadmin
  • db_accessadmin
  • db_backupoperator
  • db_ddladmin
  • db_datawriter
  • db_datareader
  • db_denydatawriter
  • db_denydatareader
  • Public

db_owner

Users in the db_owner role have it all, within a single database. They can grant and revoke access, create tables, stored procedures, views, run backups, schedule jobs. Heck, a user who is db_owner can even drop the database.

However, just because you have the keys to the kingdom doesn’t mean that you can do everything. Users who have been granted db_owner will still need specific permissions to run traces and view many of the dynamic management views. Why is that? Those are managed at the server level and will require that server level permissions or roles are granted to your login.

Why use db_owner? You would want to add a user to the db_owner role if you have a user who needs to make extensive modifications to all aspects of a database - creating users, adding them to roles, creating tables/views, creating stored procedures, and adding security settings for the tables, views, and stored procedures. One example of this would be a developer who is creating a database for a new application (or creating extensive modifications to an existing application) but you don’t want to add that user to the sysadmin group.

  -- Create our sample database and switch to it
  CREATE DATABASE TestFixedRoles;
  GO
  USE TestFixedRoles;
  GO
 
  -- Set up our test users
  CREATE USER user_dbo WITHOUT LOGIN;
  CREATE USER user_security WITHOUT LOGIN;
  CREATE USER user_reader WITHOUT LOGIN;
  CREATE USER user_writer WITHOUT LOGIN;
  CREATE USER user_backup WITHOUT LOGIN;
  GO
 
  -- Grant db_owner to user_dbo
  EXEC SP_ADDROLEMEMBER N'db_owner', N'user_dbo';
  EXEC SP_ADDROLEMEMBER N'db_securityadmin', N'user_security';
 
  -- Create an orders table as dbo
  CREATE TABLE orders (
    id            INT       NOT NULL   IDENTITY(1,1),
    orderdate     DATETIME  NOT NULL,
    employee_id   INT       NOT NULL,
    customer_id   INT       NOT NULL,
    quantity      INT       NOT NULL,
    CONSTRAINT PK_Orders PRIMARY KEY (id)
  );
  GO
 
  -- temporarily switch to the context of user_dbo
  EXECUTE AS USER = 'user_dbo';
  SELECT USER_NAME(); -- This should return user_dbo
 
  -- This will return 1 row for our orders table
  SELECT t.[name] 
    FROM sys.tables AS t
   WHERE t.[name] NOT LIKE 'sys%'
 
  DROP TABLE orders;
 
  -- revert back to our regular user
  REVERT; 
  GO

db_securityadmin

Users in the db_securityadmin role can modify role permissions and manage permissions. Users in this role have, in theory, almost as much power members of db_owner. The only thing that a member of db_securityadmin can’t do is add users to the db_owner role. Members of db_securityadmin also cannot add users to fixed database roles (this requires membership in the db_owner role).

Why use db_securityadmin? You might want to use db_securityadmin when you need to grant privileges to a trusted user and allow them to manage privileges across an application. Take care when granting db_securityadmin to make sure that you trust the user to not give themselves additional permissions. This risk can be alleviated by adding auditing to the database to log when privileges are granted or revoked.

  EXECUTE AS USER = 'user_security';
 
  -- this will generate three errors since user_security isn't a member of db_owner.
  EXEC SP_ADDROLEMEMBER N'db_datareader', N'user_reader';
  EXEC SP_ADDROLEMEMBER N'db_datawriter', N'user_writer';
  EXEC SP_ADDROLEMEMBER N'db_backupoperator', N'user_backup';
 
  REVERT;
  GO
 
  -- this will now succeed since you are a member of db_owner, after all
  EXEC SP_ADDROLEMEMBER N'db_datareader', N'user_reader';
  EXEC SP_ADDROLEMEMBER N'db_datawriter', N'user_writer';
  EXEC SP_ADDROLEMEMBER N'db_backupoperator', N'user_backup';

db_accessadmin

Members of the db_accessadmin role have the ability to change database access. They can grant and revoke access to Windows logins, Windows groups, and SQL Server logins. The users that they grant access to will be members of the Public role and will have all the privileges associated with that role.

Why use db_accessadmin? This role would be used when you have a user who is responsible for maintaining access to a specific database. When combined with db_securityadmin you have a user who is capable of granting and revoking general access to a database as well as controlling the security permissions for almost any user. This combination is quite powerful and should be granted carefully. With auditing in place, you can mitigate any risk of granting both roles to a single user.

db_backupoperator

Members of this role can create database backups. It’s important to note that they cannot, by default, restore the backups that they create. The only users that can restore a backup are members of the sysadmin and dbcreator server roles and the owner of the database (dbo).

Why use db_backupoperator? If you have an automated process that connects to the database and creates a backup it would be a good idea to have all backup operations connect to the database using a user/login that only has db_backupoperator access to prevent any unauthorized data access due to a user being compromised.

db_datareader/db_denydatareader

Members of the db_datareader role are able to read all data from all user tables. That’s right, everything. Even the super secret secrets.UserPayHistoryAndSocialSecurityNumbers table can be read by members of db_datareader.

Conversely, members of db_denydatareader are explicitly denied the ability to write to any user created tables. They live in the dark about the contents of the database.

Why use db_datareader? Let’s say the accounting department has a separate database. Everyone in accounting is able to create and run ad hoc reports directly against this database, but they shouldn’t be able to do anything else apart from see the contents of the database. Clearly granting db_owner access is out of the question. Your junior DBA is out sick today, so you can’t make him grant SELECT permissions to every table in the database while you go out to Chipotle for lunch. Instead, you can simply grant db_datareader access to the accounting department’s Windows group.

Why use db_denydatareader In addition to the accounting department’s requirement to be able to create ad hoc reports as needed, HR needs to be able to run the canned reports available to them through your carefully crafted stored procedures but they should not be able to run ad hoc reports. However, it turns out that they are also able to create and run ad hoc reports (thankfully your junior DBA emailed you about this yesterday before he got sick). By granting db_denydatareader permissions to the HR Windows group you can prevent members of the HR group from running the ad hoc reports (assuming Windows Forms authentication is being used) and get in touch with the application developers to have them hide ad hoc reports from the HR group in the application front end.

db_datawriter/db_denydatawriter

Much like db_datareader and db_denydatareader, the name of this role is largely self-explanatory: members of the db_datawriter role can INSERT, UPDATE, and DELETE data from any user created table. Likewise, members of the db_denydatawriter role are explicitly denied the ability to perform INSERT, UPDATE, and DELETE operations on any user created tables.

Why use db_datawriter? db_datawriter would be a good choice for a user or login that runs an automated ETL process on a regular basis. This ensures that access to all tables is maintained even when new tables are added and reduces maintenance overhead.

Why use db_denydatawriter If you want to limit write access for a login or user it is easy to add them to the db_denydatawriter role and allow specific access to a subset of tables. This might be desirable when users may need to make adjustments to certain tables - such as a bill, order, or account balance - but they do not need the ability to modify all data in the database.

db_ddladmin

Members of the db_ddladmin role are able to execute DDL commands (CREATE, ALTER, DROP) within the current database. It’s pretty self-explanatory - a member of db_ddladmin can run any CREATE, ALTER, or DROP command within the current database. They cannot create new databases, nor can they alter or drop the current database.

Why use db_ddladmin? Your company has brought on several consultants to help develop a new application. Their work will require that they are able to create, or change, new tables, views, indexes, and stored procedures. However, they don’t need the ability to create users or manage security. In this case, your best solution would be to add the consultants to the db_ddladmin role.

Public

The Public role is a bit different from all of the other roles. Every database user is a member of the Public role. If a user does not have any explicit permissions on a database object, they will inherit the permissions of the Public role.

Why use the public role? Going back to our example of the Accounting and HR departments, let’s say that there are now two databases: Accounting and HR. For the purposes of this example, only Accounting personnel should have access to the Accounting database and only HR personnel should have access to the HR database. What’s the best way to accomplish this?

The first step is to create a user for each department in their respective database. This user needs to be mapped to the appropriate Windows group. Once you have created the user and mapped it to the appropriate group, you can then add the user to the Public role.

Using this method it’s easy to add additional users and groups to the Public role without having to manage separate security settings for each one individually.

  /*****************************************************************************
   * PUBLIC ROLE DEMONSTRATION
   ****************************************************************************/
  CREATE DATABASE Accounting;
  GO
  USE Accounting;
  GO
  -- This is going to fail unless you have a LOGIN called group_Accounting
  CREATE USER user_Accounting FOR LOGIN group_Accounting;
  GO
  EXEC SP_ADDROLEMEMBER N'Public', N'user_Accounting';
  GO
 
  CREATE DATABASE HumanResources;
  GO
  USE HumanResources;
  GO
  -- This is going to fail unless you have a LOGIN called group_HumanResources
  CREATE USER user_HumanResources FOR LOGIN group_HumanResources;
  GO
  EXEC SP_ADDROLEMEMBER N'Public', N'user_HumanResources';
  GO

Fourth Blog Birthday

Wednesday, December 31st, 2008

For the second year running I'm late celebrating my blog birthday (it was yesterday); my only excuse is that I'm still reeling from the amount I've eaten over the last two weeks. But four years of blogging... wow... it feels like ages.

And to a certain extent I feel that, after all this time, I'm running out of things to say here. The actual writing of blog entries isn't a problem, it's more the problem of having something to write about. Part of the problem is me: I don't want to write about things I don't find interesting so I haven't gone down the route of turning the blog into a MDX tutorial (Bill Pearson does that much better than I ever could, and only Mosha could ever cover the advanced stuff properly), but at the same time I'm not coming across so many MDX/SSAS issues or obscure features as I used to. Part of the problem is, too, that SSAS2008 was so light on new features that it didn't provide me with much new to write about. So I'm hoping that Gemini, Kilimanjaro, Excel 14, Azure etc will give me something to get my teeth into in 2009; I'm sure they will. If not, well, I've always wanted to spend some time getting into Mondrian and other open source BI technologies. And with the economy the way it is I suppose I'll have a lot more spare time for learning new stuff in the coming twelve months...

But anyway, bear with me and keep reading! For those of you who have stuck with me for the last four years, thanks, and best wishes for 2009.

Report of User Permissions

Wednesday, December 31st, 2008

This is a custom report developed to list user permissions.

Often DBA's need a consolidated list of permissions for a user, this custom report does this.  Developed by Tom Reeves.

Here's a report you can use to see permissions for a given user or role in SQL2005.

Save the attached file to the following location. This is where custom reports have to reside for them to work. Where you see "{user}" should be changed to whatever your username is.

C:\Documents and Settings\{user}\My Documents\SQL Server Management Studio\Custom Reports

User_Permissions.txt - Right click and save with a RDL Extension. (37.25 KB)

Move the Reporting Services Log files and Dump Files

Wednesday, December 31st, 2008

Moving the log files in reporting services is an often overlooked step, until after 10-14 months when suddenly your 8gb c drive is full !

Add the following line of code to the listed config files.

Code:

<add name="Directory" value="E:\Reporting Services Dump Files\" />

Files:

C:\Program Files\Microsoft SQL Server\MSSQL.1\Reporting Services\ReportServer\bin\ReportingServicesService.exe.config

C:\Program Files\Microsoft SQL Server\MSSQL.1\Reporting Services\ReportServer\web.config

C:\Program Files\Microsoft SQL Server\MSSQL.1\Reporting Services\ReportManager\web.config

Thanks to Tom Reeves for solving this problem.

SQL Server 2005 SP3

Wednesday, December 31st, 2008

Just a quick placeholder on the progress of SQL Server 2005 service pack 3.

This update is important, if nothing else to ensure your on the latest patch which addresses some security vulnerabilities.

There is also a cumulative update for service pack 3, generally I don't go down the cumulative update path unless i've been advise by Microsoft (ie, it addresses a problem being experienced) or the SQL Community in general highly recommends a specific CU.

I always start with patching my local developer edition, x86 Instance.

Slowly working through a few boxes, so far we've patched the following with no issues:

  • 64 Bit named instances running under Polyserve 3.6.1 - both dev, qa and production, no issues, Enterprise Edition.
  • 64 Bit named instance stand-alone server, Enterprise Edition.
  • 64 Bit named instance stand-alone server, Standard Edition.
  • 64 Bit Clustered SQL Server, Active / Active Configuration, no issues, did require reboot of both nodes and fail over, Enterprise Edition.
  • 32 Bit SSIS stand alone installations (no dbms) no issues.
  • 32 Bit Reporting Services Web Farm, authentication issues afterwards.

I had an issue with the Reporting Services Web Farm, which uses SQL Authentication for connectivity, caused the login to be disabled.  I had quite a difficult time getting it re-enabled and changing the password, but no errors in applying the service pack.

Good information on upgrade to 2008

Wednesday, December 31st, 2008

The SQL Server Customer Advisory Team has a great blog and they have a good article here about upgrading to 2008. Lot of reasons to upgrade to 2008 (especially if you are on SQL Server 2000 or earlier). Check it out; http://blogs.msdn.com/sqlcat/archive/2008/12/08/ultimate-guide-to-upgrading-to-sql-server-2008.aspx

Congrats to Tom LaRock and Pat Wright, now on the PASS Board!

Wednesday, December 31st, 2008

The PASS Board of Directors election was held in November, and SQLServerPedia BI Editor Douglas McDowell was elected to serve on the board.

Yesterday, Pat Wright and SQLServerPedia Monitoring Editor Tom LaRock got a pleasant surprise – they’ve been appointed as well!  Two of the board members had resigned, and Pat and Tom were appointed to those posts.  Our congratulations go out to both new board members!

Good use for WITH RECOMPILE

Tuesday, December 30th, 2008

Haven't looked into all of the details underneath but at the surface probably a good reminder of where WITH RECOMPILE can be helpful...

Using SQL Server Reporting Services, a workaround was used (this is the part I want to look into more) to basically get a blank report showing when a user first links to a report until they change parameters and run the report themselves. So this first execution gives an false year/month to search back until (190001). The procedure behind this report looks for any "invalid" (per the business rules that means less than 195001) date and issues a "RETURN", returning control to the color.

The problem was picked up during an execution after a restart. When the Reporting Services query ran asking for 200809 it ran until it timed out, no results coming back. When the query was executed for 200809 from SQL Server Management Studio, it came back in about 30 seconds.

Two issues at play here 1.) The sessions are getting different query plans (topic for another post.. various reasons this can happen even for the same proc) and 2.) The query plan used by SSRS is being compiled first with the first time an SSRS session ever executes the query (190001).

This can be verified by looking at the xml showplan for the execution and seeing what the parameter is compiled for.

In this case, adding WITH RECOMPILE to the stored procedure before the AS, tells SQL to recalculate the plan at each execution instead of cache and reuse a plan for every execution. This resulted in the reporting services query coming back in 35 seconds as opposed to timing out.

Obviously longer term solutions include figuring out why this was designed this way and looking at other possible options. Normally WITH RECOMPILE is best when working with queries that are called with dramatically different parameter values each time that cause differences in the way the plan is figured.

For another post, we'll talk about how you can have the same procedure use different plans even for the same server and how to minimize/avoid that.

Good use for WITH RECOMPILE

Tuesday, December 30th, 2008

Rolling Back (or is it??)

Tuesday, December 30th, 2008

Interesting Issue Today/Yesterday -

A query was using a linked server to grab data from DB2 via an ODBC connection. Normal execution time is about 15 minutes. This had run on for at least 7 hours when it was decided to kill it, appeared to be doing nothing but causing blocks (due to Intent Shared and Intent Exclusive locks preparing to do an update if the data ever came back).

Waited a few hours, still didn't die so we figured we could wait, if it took 7 hours prior to killing, rollback could potentially take at least that long if not much longer.

Came back today and it was still running.. By this time now about 30 hours since initial query executed and 23 hours after KILL issued. Process state was rollback.

When I looked at the stats for the session (SELECT * FROM SYS.DM_EXEC_REQUESTS), I noticed the CPU counter incrementing so it was at least thinking about doing something if not in the middle of doing anything. Rollback just didn't make sense to be so long knowing what the query did.

Left me with few options though:

  • Restart SQL Service - risk this database coming back up in recovery and not available at all
  • Something horrible and drastic - Not a great troubleshooting methodology so put this out of my mind as a serious option... But rebuilding the log would probably have "solved" the immediate issue while potentially leaving a database in a transactionally inconsistent state
  • Wait and hope - Safest but due to timelines of necessary processing was not a truly viable option.

So I called MS Support. Great assistance (as normal from SQL support) but confirmed what I was thinking: no help. They had no tools to undo this and had the three options I had above at first. They came back with a 4th I hadn't considered:

  • Take a full backup, restore it with a new name, verify everything appears right and cut over to that database (change name of DB with rollback operation, change name again of the restored DB).

The escalatio engineer had a good point: when you take the full backup that transaction that is rolling back was never comitted and would not be grabbed by that backup so the restore will leave a consistent and ready database.

Before going that route, I searched some more on the issue and after looking at what it was doing (again not a lot of work) and some common repeats of this issue (when using a linked server some folks reported hung queries that on rollback after kill would be in rollback state for as long as 7 days before they gave up and restarted). So I decided to take two of the options to mitigate the risk. Feeling confident that a restart would probably be the end all I:

  1. Backed up the offending database to use the 4th option from MSFT PSS if need be
  2. Went through the change notification process for the service recycle
  3. Restarted SQL Service
  4. Held my breath and
  5. Presto! The database recovered instantly in a good state
  6. DBCC CHECKDB run to ensure DB free of issues that would be picked up here
  7. Had users query data to ensure it seemed right
  8. Back in business

In keeping with the thought of mitigation, I will be looking into more behind the why, investigate the ODBC drivers and versions being used here, attempt to recreate the same issue in a different environment and remember this blog post is here if I need it...