By: Aaron Bertrand | Last Updated: 2014-10-13 | Comments (3) | Security
I occasionally see people troubleshooting some problem in Windows, where the only information they have is the Windows SID, in the form S-1-5-21-601521654-1178811285-3323947944-1000. Trying to map that to the SID belonging to a login or group in SQL Server is difficult, because they are in a different form, for example: 0x010500000000000515000000F67DDA23953B4346A86B1FC6E8030000. You certainly cannot eyeball that and identify a match, and even manual conversion between these two values is not exactly straightforward.
Based almost entirely on some work done long ago in a forum thread here, I thought that it would make a lot of sense to have a view over sys.server_principals - perhaps in your own utility database - which also exposed the Windows version of the SID. This would make it far easier to troubleshoot issues like this, even if they don't come up very often.
The forum thread above uses two scalar functions to map back and forth, but I am a big stickler for inline, table-valued functions. The benefit, in simplistic terms, is the way inline functions are optimized as part of the plan for the query, unlike scalar functions, which are called per row and optimized separately. Not that this is a situation where you need screaming performance, but it's a good behavior to follow in general.
And we actually only need one function here, because we will always have the SQL version of the SID in sys.server_principals. In order to create the function, though, first we will need a numbers table (this allows us to treat segments of the Windows SID string as a set instead of parsing them manually in an explicit loop):
CREATE TABLE dbo.TinyNumbers(Number TINYINT PRIMARY KEY); INSERT dbo.TinyNumbers(Number) SELECT TOP (256) ROW_NUMBER() OVER (ORDER BY number)-1 FROM master.dbo.spt_values;
We could generate a sequence of numbers in the function, too, without having some table of numbers to draw from, but that would either prevent us from marking the function as SCHEMABINDING (if we were to use a system view like spt_values instead), or lead to much more verbose looping or CTE-type code to generate the sequence without the assistance of any auxiliary objects. Here is the function:
CREATE FUNCTION dbo.GetWindowsSID ( @sid VARBINARY(85) ) RETURNS TABLE WITH SCHEMABINDING AS RETURN ( SELECT ADsid = STUFF((SELECT '-' + part FROM ( SELECT Number = -1, part = 'S-' + CONVERT(VARCHAR(30),CONVERT(TINYINT,CONVERT(VARBINARY(30),LEFT(@sid,1)))) + '-' + CONVERT(VARCHAR(30),CONVERT(INT,CONVERT(VARBINARY(30),SUBSTRING(@sid,3,6)))) UNION ALL SELECT TOP ((LEN(@sid)-5)/4) Number, part = CONVERT(VARCHAR(30),CONVERT(BIGINT,CONVERT(VARBINARY(30), REVERSE(CONVERT(VARBINARY(30),SUBSTRING(@sid,9+Number*4,4)))))) FROM dbo.TinyNumbers ORDER BY Number ) AS x ORDER BY Number FOR XML PATH(''), TYPE).value(N'.','nvarchar(max)'),1,1,'') ); GO
Then we can simply create a view that pulls certain columns from sys.server_principals, and passes the SID to the function via CROSS APPLY:
CREATE VIEW dbo.server_principal_sids AS SELECT sp.name, sp.[sid], ad.ADsid, sp.type_desc FROM sys.server_principals AS sp CROSS APPLY dbo.GetWindowsSID(sp.[sid]) AS ad WHERE [type] IN ('U','G') AND LEN([sid]) % 4 = 0;
Now I can simply say:
SELECT name,[sid],ADSid,type_desc FROM dbo.server_principal_sids;
And I will get results like this:
Disclaimer: The function does a very good job at parsing Windows-based SIDs because they all happen to map to a very particular pattern (S-1-5-... see KB #163846 for details). For things like SQL logins, server roles and certificate-mapped logins, the pattern can be different, and the function does not account for those differences, leading to errors or bogus values. This is why the filters are in place in the view, so please use this function with caution if you are trying to generate Windows-looking SIDs from anything other than Windows logins - you will likely need to make adjustments for that to work.
- Bookmark this page, in case you ever have a need to map a Windows S-1-5-... SID to the equivalent varbinary SID inside SQL Server.
- Review the following tips and other resources:
- Identifying the Tie Between Logins and Users
- Understanding and dealing with orphaned users in a SQL Server database
- SQL Server Security Auditing to Match Logins And Users
- Using schema binding to improve SQL Server UDF performance
- KB #163846 : SID Values For Default Windows NT Installations (Microsoft Knowledge Base)
- sys.server_principals (Transact-SQL) (MSDN)
- Grouped Concatenation in SQL Server
- Four Practical Use Cases for Grouped Concatenation
Last Updated: 2014-10-13
About the author
View all my tips