Monday, July 30, 2012

How to Audit Login Changes on a SQL Server


For instance, if you want to detect what logins have been created, deleted, or modified since the last audit, a purely T-SQL solution can be implemented quickly and easily. We'll tackle something that provides slightly more information in this tip.
First, the basic tables we'll need to do the auditing. We'll need a table to store the current scan information as well as a table to store our scan history. We'll actually compare the two tables in order to determine what has changed between audits.  We'll also need a table to track when we did our scans.
Here are the three tables:

CREATE TABLE dbo.AuditScan (
 AuditScanID INT IDENTITY(1,1) NOT NULL,
 ScanStartDate datetime NOT NULL,
 ScanEndDate datetime NULL,
 CONSTRAINT PK_AuditScan PRIMARY KEY CLUSTERED (AuditScanID)
);
GO 



CREATE TABLE dbo.CurrentLoginScan (
 [sid] VARBINARY(85) NOT NULL,
 [name] sysname NOT NULL,
 [type] CHAR(1) NOT NULL,
 create_date DATETIME,
 modify_date DATETIME,
 AuditScanID INT NOT NULL,
 CONSTRAINT PK_CurrentLoginScan PRIMARY KEY CLUSTERED ([sid]),
 CONSTRAINT FK_CurrentLoginScan_AuditScan FOREIGN KEY (AuditScanID) 
 REFERENCES dbo.AuditScan (AuditScanID)
);
GO 



CREATE TABLE dbo.LoginScanRecord (
 [ScanLogID] INT NOT NULL IDENTITY(1,1),
 [AuditScanID] INT NOT NULL,
 [sid] VARBINARY(85) NOT NULL,
 [name] sysname NOT NULL,
 [type] CHAR(1) NOT NULL,
 create_date DATETIME NOT NULL,
 modify_date DATETIME NOT NULL,
 CONSTRAINT PK_LoginSCanRecord PRIMARY KEY CLUSTERED (ScanLogID),
 CONSTRAINT FK_LoginScanRecord_AuditScan FOREIGN KEY (AuditScanID) 
 REFERENCES dbo.AuditScan (AuditScanID)
);
GO 
Now we'll need a couple of stored procedures to mark the start and stop of each audit.
CREATE PROC dbo.StartAuditScan 
  @AuditScanID INT OUTPUT
AS
BEGIN
  SET NOCOUNT ON;

  
  DECLARE @AuditScanIDTable TABLE(AuditScanID INT);

  
  INSERT INTO dbo.AuditScan (ScanStartDate) 
    OUTPUT INSERTED.AuditScanID INTO @AuditScanIDTable
  VALUES (GETDATE());

  
  SET @AuditScanID = (SELECT AuditScanID FROM @AuditScanIDTable);
END;
GO 



CREATE PROC dbo.EndAuditScan
  @AuditScanID INT
AS
BEGIN
  SET NOCOUNT ON;

  
  UPDATE dbo.AuditScan
  SET ScanEndDate = GETDATE()
  WHERE AuditScanID = @AuditScanID;
END;
GO 
And finally, we'll need the stored procedure that does the audit itself. It's going to do the following:
  1. Start the audit.
  2. Retrieve the pertinent information from the sys.server_principals view into the CurrentLoginScan table.
  3. Compare to see what new logins have been created since the last audit.
  4. Compare to see what logins have been deleted since the last audit.
  5. Compare to see what logins show a change since the last audit.
  6. End the audit.
  7. Report the findings.
Here's the stored procedure that accomplishes the above.
CREATE PROC dbo.ExecuteLoginScan
AS
BEGIN
  SET NOCOUNT ON;

  
  DECLARE @CurrentScanID INT;
  DECLARE @PreviousScanID INT;

    
  SET @PreviousScanID = (SELECT MAX(AuditScanID) FROM dbo.AuditScan);
  EXEC dbo.StartAuditScan @CurrentScanID OUTPUT;

    
  EXEC dbo.EndAuditScan @CurrentScanID;

  
  INSERT INTO dbo.CurrentLoginScan
  ([sid], [name], [type], create_date, modify_date, AuditScanID)
  SELECT [sid], [name], [type], create_date, modify_date, @CurrentScanID
  FROM master.sys.server_principals;

    
  CREATE TABLE #LoginScanReport (
    Change VARCHAR(20) NOT NULL,
    [sid] VARBINARY(85) NOT NULL,
    [Current_Name] sysname NULL,
    [Old_Name] sysname NULL,
    [Current_Type] CHAR(1) NULL,
    [Old_Type] CHAR(1) NULL,
    [Current_Create_Date] DATETIME NULL,
    [Old_Create_Date] DATETIME NULL,
    [Current_Modify_Date] DATETIME NULL,
    [Old_Modify_Date] DATETIME NULL
  );

    
  -- Find new logins
  INSERT INTO #LoginScanReport 
  (Change, [sid], Current_Name, Current_Type, Current_Create_Date, Current_Modify_Date)
  SELECT 'Added', Curr.[sid], Curr.[name], Curr.[type], Curr.create_date, Curr.modify_date
  FROM dbo.CurrentLoginScan Curr
    LEFT JOIN (SELECT [sid], [name], [type], create_date, modify_date 
               FROM dbo.LoginScanRecord 
               WHERE AuditScanID = @PreviousScanID) Old
      ON Curr.[sid] = Old.[sid]
  WHERE Old.[sid] IS NULL;

  
  -- Find deleted logins
  INSERT INTO #LoginScanReport 
  (Change, [sid], Old_Name, Old_Type, Old_Create_Date, Old_Modify_Date)
  SELECT 'Deleted', Old.[sid], Old.[name], Old.[type], Old.create_date, Old.modify_date
  FROM dbo.CurrentLoginScan Curr
    RIGHT JOIN (SELECT [sid], [name], [type], create_date, modify_date 
               FROM dbo.LoginScanRecord 
               WHERE AuditScanID = @PreviousScanID) Old
      ON Curr.[sid] = Old.[sid]
  WHERE Curr.[sid] IS NULL;

      
  -- Find Changed Logins
  INSERT INTO #LoginScanReport 
  (Change, [sid], Current_Name, Current_Type, Current_Create_Date, Current_Modify_Date,
                  Old_Name, Old_Type, Old_Create_Date, Old_Modify_Date)
  SELECT 'Modified', Curr.[sid], Curr.[name], Curr.[type], Curr.create_date, Curr.modify_date,
                     Old.[name], Old.[type], Old.create_date, Old.modify_date
  FROM dbo.CurrentLoginScan Curr
    JOIN (SELECT [sid], [name], [type], create_date, modify_date 
          FROM dbo.LoginScanRecord 
          WHERE AuditScanID = @PreviousScanID) Old
      ON Curr.[sid] = Old.[sid]
  WHERE (Curr.[name] <> Old.[name]) OR (Curr.[type] <> Old.[type]) 
         OR (Curr.create_date <> Old.create_date) OR (Curr.modify_date <> Old.modify_date);

           
  INSERT INTO dbo.LoginScanRecord
  (AuditScanID, [sid], [name], [type], create_date, modify_date)
  SELECT AuditScanID, [sid], [name], [type], create_date, modify_date
  FROM dbo.CurrentLoginScan;

    
  DELETE FROM dbo.CurrentLoginScan;

  
  SELECT Change, [sid], Current_Name, Old_Name, Current_Type, Old_Type, 
    Current_Create_Date, Old_Create_Date, Current_Modify_Date, Old_Modify_Date
  FROM #LoginScanReport;  
END;
GO

Saturday, July 28, 2012

What is the difference between ORIGINAL_LOGIN() and SUSER_SNAME() and when will I use it?

What is the difference between ORIGINAL_LOGIN() and SUSER_SNAME() and when will I use it?
__________________________________________________________________________________


Function ORIGINAL_LOGIN() returns the name of the original or very first login that connected to the instance of SQL Server and it is used to identity of the original login in sessions. If there is an application or database where context switching is happening quite often this can be a very useful feature for auditing. Let us see a quick T-SQL example where we initially retrieve current user and original login together you will notice that they are same. Afterwards we will create a new user and we will set the context of the execution to the new user. Later we will run the same command and you will notice that the current user name has been changed but the original login remains the same.
USE AdventureWorks2012;GO-- Get Login DetailsSELECT ORIGINAL_LOGIN() OriginalLogin, SUSER_SNAME() CurrentLogin
GO
--Create a temporary login and user.CREATE LOGIN TestLogin WITH PASSWORD = 'Complex@1';CREATE USER TestUser FOR LOGIN TestLogin;GO-- Execute as another userEXECUTE AS LOGIN = 'TestLogin'GOSELECT ORIGINAL_LOGIN() OriginalLogin, SUSER_SNAME() CurrentLogin
GO
-- Revert Execution as another userREVERT
GO
-- Get Login DetailsSELECT ORIGINAL_LOGIN() OriginalLogin, SUSER_SNAME() CurrentLogin
GO
-- Clean upDROP LOGIN TestLogin;DROP USER TestUser;GO

This feature can be very useful in the auditing as well figuring out who is the original user to the session.

Defining and Establishing SQL Server Policies and Procedures

ProblemAs your IT team grows more and more people may have direct impact on your SQL Server databases.  At the onset it may have been just a couple of developers and a DBA, but as your company becomes more successful and more resources are put on developing, maintaining and supporting a database driven application the potential for inconsistent processes and undocumented changes increases.  Just when you need more control and structure things tend to sway the other way, because now there are more people and components that need to be managed.  So what is the best process to put controls in now so when things grow beyond one or two people chaos does not follow?
SolutionWith most IT processing it is often essential to document your policies with standardized procedures.  In most cases there are already established rules, whether they are formalized or not there is some type of rules or procedures that are followed on a day to day basis. One potential problem with this un-formalized approach is that one person may have their set of procedures and another person may have a totally different set of procedures.  As mentioned already with a small group of people this may not be as big an issue, but as more and more people perform the same tasks you are bound to have further discrepancies.
Define a standardized format The first step in formalizing the process is to document your already established rules, although they may be loosely defined there is some process that you are already following.  In order to document your policies and procedures it is helpful to have a standard format that you follow.  This can simply be a Word document or database application that you develop.  Whatever the approach, you should establish a standard template for documentation.
Here is a simple format that can be used to document your policy and procedures.  This is just a starting point and you can add or remove whatever items make sense.

PolicyName of the policy.
Effective DateDate policy became effective.
PurposeShort description of why this policy has been created.
AffectedIndividuals or groups of people affected by the policy.
ReferencesReference to other existing policies
DefinitionsDefinitions of certain key words to provide context.
ProceduresProcess that should be followed.

To take this a step further, here is a sample policy for changing the "sa" password.

PolicyQuarterly SQL Server "sa" password change
Effective Date8/9/2007
PurposeThe SQL Server "sa" password is used only as a last resort to access the database server.  To ensure that this password is secure and not used by unauthorized users the password will be changed on a quarterly basis.
AffectedSQL Server database administrators
ReferencesPolicy 56B - Use of SQL Server "sa" account Policy 424A - Strong password policy
Definitions"sa" - the default SQL Server system administrator account
ProcedureThe password should be changed as follows:
  1. Changed on the first Monday of the following months (January, April, July, October)
  2. The lead DBA on duty that day is responsible for the change
  3. The new password must conform to our strong password policy (424A)
  4. The password change must be logged in our change management application
  5. The new password is locked in safe for emergency use only

As you can see from the sample policy this is not that difficult.  Although this example is pretty simple, if you document things in a step by step approach you should be able to document most procedures without much problem. 
You may also think that since you are the only one responsible for maintaining your systems that going through this process is not worth it.  Think back to a process that you ran a year ago and you know that you will need to run again.  Wouldn't be easier to just document the steps now and then revert back to the policy when you need to duplicate the steps?  Also, you need to think about the potential for more people coming on board and the impact this will have if your policies are not documented.  Also, if you document the policies now, when new people do come on board you can ensure that they follow what you have already outlined by just giving them the well documented procedures.
One common pitfall when documenting things is the assumption that other people know what you are talking about.  Based on this it is very easy to gloss over the details and just write some high level procedures.  Therefore it is helpful to have someone else review the process and even approve the process.  By having someone else read and understand what has been documented there are at least two people now that understand the policy as written.
Standardized ApproachNow that you have formalized some of your processes you can guarantee that the approach that is used is standardized as well.  This can really be for just about everything that affects SQL Server such as:
  • applying service packs
  • deploying database changes
  • adding logins and users
  • sysadmin level access
  • object naming standards
  • coding documentation standards
  • standard login passwords
  • use of standard logins
  • etc...
As you can see a lot of the things that you do on a day to day basis you already have a standard approach for how to address and tackle these issues. Take the time and formalize the approach and ensure that going forward everyone adheres to the same rules.


Different techniques to identify blocking in SQL Server

Problem
SQL Server is able to service requests from a large number of concurrent users. When SQL Server is servicing requests from many clients, there is a strong possibility that conflicts will occur because different processes request access to the same resources at the same time. A conflict in which one process is waiting for another to release a resource is called a block. Although in SQL Server a blocked process usually resolves itself when the first process releases the resource but there are times when a process holds a transaction lock and doesn’t release it. In this tip, we will learn different techniques to troubleshoot and resolve blocks in SQL Server.
Solution
In order to resolve a blocked process, we first need to determine which process is the blocking process and then if possible kill the blocking process. There are many different ways in SQL Server to identify a blocks and blocking process that are listed as follow:
  • Activity Monitor
  • SQLServer:Locks Performance Object
  • DMVs
    • sys.dm_exec_requests
    • sys.dm_tran_locks
    • sys.dm_os_waiting_tasks
  • SQL Server Profiler Locks Event Category
Each of these tools reports different information which is helpful in resolving blocks quickly. Let’s have a look at these tools in details:
1) Activity Monitor
Activity Monitor is a tool in SQL Server Management Studio that gives you a view of current connections on SQL Server. You can use Activity Monitor to view information about the current processes and locks held on SQL Server resources. To open Activity Monitor in SQL Server Management Studio, right-click the SQL Server instance name in Object Explorer and then select Activity Monitor:
Launch Activity Monitor
To find blocked process with Activity Monitor, first click on Processes in Activity Monitor to open the Process Info page:
Process Info Page and Locating Blocking Process
Then locate the process that is waiting, and then scroll over to the Blocked By column and note the Process ID in that column. Find that Process ID in Process Info page. and
If you want to terminate the blocking process, right-click it and choose Kill Process:
2) The SQLServer:Locks performance object
You use SQLServer:Locks object counter in Performance Monitor to view current statistics or create a log or alert to monitor locks. For example, you can monitor Average Wait Time, Number of deadlocks/sec and Lock Timeouts/sec statistics to determine whether there is a problem with resource contention on SQL Server. However, you will need additional information to determine the exact cause of the problem. Follow the steps below to monitor the SQLServer: Locks performance counter:
On the Start menu, point to Run, type perfmon in the Run dialog box, and then click OK to launch Performance Monitor.
Launching Performance Monitor
·  Right-click anywhere on the screen and then choose Add Counters.
 
Add counters
·  Scroll down to locate SQL Server lock counters and add these three counters and then click OK.
  • Average Wait Time
  • Number of deadlocks/sec
  • Lock Timeouts/sec
3) DMVs (Dynamic Management Views)
sys.dm_exec_requests
You can use the sys.dm_exec_requests dynamic management view to obtain detailed information about the requests currently executing on SQL Server. The dynamic management view includes detailed information about the query and query plan, status of request and information about the amount of time it has been executing. The columns you are most likely to use when troubleshooting a block or deadlock are as follow:
  1. blocking_session_id - The SPID of the blocking session.
  2. wait_type - Type of wait.
  3. wait_time - Length of time request has been waiting (in milliseconds).
  4. last_wait_type - If a wait has ended, its type is listed here.
  5. wait_resource - Name of resource the request is waiting for.
  6. transaction_isolation_level - Isolation level for the transaction.
  7. lock_timeout - Length of time a lock can exist before timing out
To view blocked process execute the following query:
USE [master]
GO
SELECT  session_id
 ,blocking_session_id
 ,wait_time
 ,wait_type
 ,last_wait_type
 ,wait_resource
 ,transaction_isolation_level
 ,lock_timeout
FROM sys.dm_exec_requests
WHERE blocking_session_id <> 0
GO
sys.dm_tran_locks
You can view information about current locks and the processes blocking them using the sys.dm_tran_locks dynamic management view. This column has one of three values: GRANT, WAIT or CONVERT. The value of CONVERT means that the requestor has been granted a request but is waiting to upgrade to the initial request to be granted. To locate information about all locks with a request status of CONVERT, you execute the following:
USE [master]
GO
SELECT * from sys.dm_tran_locks
WHERE request_status = 'CONVERT'
GO
The request_session_id column contains the Process ID for the process. To view locking in the particular database, execute the following query that joins sys.dm_tran_locks with sys.partitions:
USE [master]
GO
SELECT   tl.resource_type
 ,tl.resource_associated_entity_id
 ,OBJECT_NAME(p.object_id) AS object_name
 ,tl.request_status
 ,tl.request_mode
 ,tl.request_session_id
 ,tl.resource_description
FROM sys.dm_tran_locks tl
LEFT JOIN sys.partitions p
ON p.hobt_id = tl.resource_associated_entity_id
WHERE tl.resource_database_id = DB_ID()
GO
sys.dm_os_waiting_tasks
The sys.dm_os_waiting_tasks dynamic management view reports information about the blocked and blocking processes. The blocked process is listed in the session_id column. The blocking is listed in the blocking_session_id column.
Execute the following to view wait stats for all block processes on SQL Server:
USE [master]
GO
SELECT   w.session_id
 ,w.wait_duration_ms
 ,w.wait_type
 ,w.blocking_session_id
 ,w.resource_description
 ,s.program_name
 ,t.text
 ,t.dbid
 ,s.cpu_time
 ,s.memory_usage
FROM sys.dm_os_waiting_tasks w
INNER JOIN sys.dm_exec_sessions s
ON w.session_id = s.session_id
INNER JOIN sys.dm_exec_requests r
ON s.session_id = r.session_id
OUTER APPLY sys.dm_exec_sql_text (r.sql_handle) t
WHERE s.is_user_process = 1
GO
This detail is good for a big picture, or to get a quick idea of the types of waits occurring, but most of the real diagnostics and tuning will occur at a statement level.
4) SQL Server Profiler
You use the SQL Server Profiler Locks event category to create a trace of events related to locks and deadlocks. You can choose one or more of these event classes:
  1. Deadlock_Graph_Event_Class — Creates an XML description of deadlocks.
  2. Lock:Acquired — Use in conjunction with Lock:Released to determine the types of locks being requested and the length of time they are retained.
  3. Lock:Cancel — Use to determine which locks are cancelled.
  4. Lock:Deadlock Chain — Use to determine the objects involved in a deadlock.
  5. Lock:Deadlock — Use to determine the objects and applications involved in a deadlock.
  6. Lock:Escalation — Reports information about locks that have been escalated to cover a larger resource. For example, when a row lock becomes a table lock.
  7. Lock:Released — Use in conjunction with Lock:Acquired.
  8. Lock:Timeout(timeout>0) — Provides information about locks that have timed out due to blocking issues.
  9. Lock:Timeout — Provides the same information as Lock:Timeout (timeout>0), but includes timeouts where the duration was 0.
5) sp_who/sp_who2
Both sp_who/sp_who2 return information about all the sessions that are currently established in the database and these are denoted as spid's. Both these store procedures accepts parameters. The blk column of sp_who and blkby column of sp_who2 contains the spid for blocking process. Running sp_who and sp_who2 is easy, for example following call of these procedures returns all process that are currently active on SQL Server:
USE master;
GO
EXEC sp_who 'active';
GO
EXEC sp_who2 'active';
GO
6) Use KILL statement to terminate blocked process
You use the KILL statement to view the status of a process or kill the process. The KILL statement has the syntax: KILL spid | UOW [WITH STATUSONLY]
USE master;
GO
KILL spid | UOW [WITH STATUSONLY]
GO
You must pass either a spid or, if the process belongs to a Distributed Transaction Coordination (DTC) transaction, you must provide a Unit of Work (UOW). You must be a member of sysadmin or processadmin to kill a process. You can obtain the spid for the current session by running @@spid. You can obtain the spid for the sessions associated with a login by running sp_who2. If you don’t specify a login, sp_who2 returns information for all current connections. If you can’t kill a blocking process, you might have to restart the SQL Server service. Doing so will cause all current connections to close, so you should avoid restarting the service if possible.