KnowledgeBase Archive

An Archive of Early Microsoft KnowledgeBase Articles

View on GitHub

Q286182: HOWTO: Use Visual Basic to Convert a Raw SID into a String SID

Article: Q286182
Product(s): Microsoft Windows NT
Version(s): 2000,4.0
Operating System(s): 
Keyword(s): kbSecurity kbDSupport kb32bitOnly
Last Modified: 08-AUG-2001

-------------------------------------------------------------------------------
The information in this article applies to:

- Microsoft Windows NT Server version 4.0 
- Microsoft Windows 2000 Server 
- Microsoft Windows 2000 Advanced Server 
-------------------------------------------------------------------------------

SUMMARY
=======

This article provides a method to convert a raw security identifier (SID) into a
string security identifier (SID) by using Microsoft Visual Basic on Windows NT
4.0 or Windows 2000.

MORE INFORMATION
================

SID Components
--------------

An SID value includes components that provide information about the SID structure
and components that uniquely identify a trustee. An SID consists of the
following components:

- The revision level of the SID structure.

- A 48-bit identifier authority value that identifies the authority, such as a
  Windows NT/Windows 2000 domain, that issued the SID.

- A variable number of subauthority or relative identifier (RID) values that
  uniquely identify the trustee relative to the authority that issued the SID.

The combination of the identifier authority value and the subauthority values
ensures that no two SIDs will be the same, even if two different SID-issuing
authorities issue the same combination of RID values. Each SID-issuing authority
issues a given RID only once.

SIDs are stored in binary format in an SID structure. To display an SID, you can
call the ConvertSidToStringSid function to convert a binary SID to string
format. To convert an SID string back to a valid, functional SID, call the
ConvertStringSidToSid function.

These functions use the following standardized string notation for SIDs, which
makes it simpler to visualize their components:

  S-R-I-S-S...

In this notation, the literal character "S" identifies the series of digits as an
SID, "R" is the revision level, "I" is the identifier-authority value, and
"S..." is one or more subauthority values.

The following example uses this notation to display the well-known
domain-relative SID of the local Administrators group:

  S-1?5-32-544

In this example, the SID has the following components. The constants in
parentheses are well-known identifier authority and RID values defined in
WINNT.H.

- A revision level of "1".

- An identifier-authority value of "5" (SECURITY_NT_AUTHORITY).

- A first subauthority value of "32" (SECURITY_BUILTIN_DOMAIN_RID).

- A second subauthority value of "544" (DOMAIN_ALIAS_RID_ADMINS).

Notice that the subauthority values are long integers. This makes converting the
raw SID into its string form very straightforward.

General Steps in the Conversion Process
---------------------------------------

1. Bind to a user object either by creating it or by using its ADsPath.

2. Retrieve the ObjectSID property.

3. Use the Convert_ObjectSID_To_SDDL function provided in the Visual Basic code
  section of this article.

  The code is very well commented. Each step in the process is explained
  carefully. If the general flow is still unclear, consult MSDN
  (http://msdn.microsoft.com/default.asp) for additional details.

How to Use the Visual Basic Code Provided
-----------------------------------------

1. Create a new Visual Basic project.

2. Create a new Visual Basic code module inside the project.

3. Copy and paste the following code into a new Visual Basic module.

Visual Basic Code to Convert Raw SID into String SID
----------------------------------------------------

  '***********************Constant Declaration*********************
  ' Define the sub SID Authority byte arrays.  These arrays are here for completeness along with
  ' their VC definitions.
  '
  '#define SECURITY_NULL_SID_AUTHORITY       {0,0,0,0,0,0}
  '#define SECURITY_WORLD_SID_AUTHORITY      {0,0,0,0,0,1}
  '#define SECURITY_LOCAL_SID_AUTHORITY      {0,0,0,0,0,2}
  '#define SECURITY_CREATOR_SID_AUTHORITY    {0,0,0,0,0,3}
  '#define SECURITY_NON_UNIQUE_AUTHORITY     {0,0,0,0,0,4}
  '
  ' C Declaration for the SID memory, included for completeness...
  '
  'typedef struct _SID {
  '  BYTE  Revision;
  '  BYTE  SubAuthorityCount;
  '   SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
  '  DWORD SubAuthority[ANYSIZE_ARRAY];
  '} SID;
  'typedef PVOID PSID;
  '***************End Constant Declaration******************************

  '**********************************************************************************
  '
  ' Declare the APIs needed to manipulate the RAW SID
  '
  Public Declare Function InitializeSid Lib "advapi32.dll" (ByVal Sid As Long, _
                                                            pIndentifierAuthority As Any, _
                                                            ByVal nSubAuthorityCount As Byte) As Long
                                                            
  Public Declare Function GetSidSubAuthority Lib "advapi32.dll" (pSid As Any, _
                                                                 ByVal nSubAuthority As Long) As Long
                                                                 
  Public Declare Function GetSidLengthRequired Lib "advapi32.dll" (ByVal nSubAuthorityCount As Byte) As Long

  Public Declare Function LookupAccountSid Lib "advapi32.dll" Alias "LookupAccountSidA" _
                                          (ByVal lpSystemName As String, _
                                           Sid As Any, _
                                           ByVal name As String, _
                                           cbName As Long, _
                                           ByVal ReferencedDomainName As String, _
                                           cbReferencedDomainName As Long, _
                                           peUse As Integer) As Long
                                           
  Public Declare Function GetSidSubAuthorityCount Lib "advapi32.dll" (pSid As Any) As Long

  Public Declare Function GetSidIdentifierAuthority Lib "advapi32.dll" (pSid As Any) As Long
  '
  ' Declare the memory managment functions.
  ' Notice there are two definitions for RtlMoveMemory, one takes a value as  Any, the other takes a value ByVal as long
  ' this is necessary so memory can be copied as follows:
  '  From a variable containing a 32 bit value that represents a memory location
  '  From a variable that represents a VB alocated memory location
  '  (ie: Dim bByte(6) as Byte type of declaration).
  '
  ' LocalAlloc and LocalFree are used to allocate and free memory from the heap,
  ' respectively.
  '
  Public Declare Sub CopyByValMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, _
                                                                           ByVal Source As Long, _
                                                                           ByVal Length As Long)
                                                                           
  Public Declare Sub CopyByRefMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, _
                                                                           Source As Any, _
                                                                           ByVal Length As Long)
                                                                           
  Public Declare Function LocalAlloc Lib "kernel32" (ByVal wFlags As Long, _
                                                     ByVal wBytes As Long) As Long
                                                     
  Public Declare Function LocalFree Lib "kernel32" (ByVal hMem As Long) As Long

  '************API Function Declarations*************************************
  '+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  '
  ' Declare the necessary Win32 APIs to obtain the SID for the NT user
  ' LookupAccountName -> Does the real work once it is remoted to the appropriated DC
  ' NetGetDcName -> Obtains the DC for the local box, used to obtain a DC name for
  '                 LookUpAccountName
  ' NetApiBufferFree-> used to cleanup after NetGetDcName.  The Net* APIs manage memory
  '                    memory for you, this function is used to free the memory allocated
  '                    NetGetDcName
  ' lstrcpyW -> used to copy the NetGetDcName buffer into a buffer VB can work with
  ' GetLengthSid -> used to help convert the raw SID into a hexstring SID
  '
  Public Declare Function LookupAccountName Lib "advapi32.dll" _
          Alias "LookupAccountNameA" _
          (ByVal IpSystemName As String, _
           ByVal IpAccountName As String, _
           pSid As Byte, _
           cbSid As Long, _
           ByVal ReferencedDomainName As String, _
           cbReferencedDomainName As Long, _
           peUse As Integer) As Long
           
  Public Declare Function NetGetDCName Lib "NETAPI32.DLL" _
          (ServerName As Byte, _
           DomainName As Byte, _
           DCNPtr As Long) As Long
                                           
  Public Declare Function NetApiBufferFree Lib "NETAPI32.DLL" _
          (ByVal Ptr As Long) As Long
           
  Public Declare Function PtrToStr Lib "kernel32" _
          Alias "lstrcpyW" (RetVal As Byte, ByVal Ptr As Long) As Long
         
  Public Declare Function GetLengthSid Lib "advapi32.dll" _
          (pSid As Byte) As Long

  '************End API Function Declarations**************************
  Sub main()
      Form1.Show
  End Sub
  '++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  ' Convert_BIN_To_SDDL takes two arguments: strNTDomain-> The NT domain for the user's account
  '                                          strNTAccount-> The NT SAM account name for the user
  ' The function returns the SDDL form of the user's SID,
  '
  Public Function Convert_BIN_To_SDDL(strNTDomain As String, strNTAccount As String) As String ' pSid As pSid, pszSidText)
      Dim SDDL_SID As String
      Dim pSia As Long
      Dim pSiaByte(5) As Byte
      Dim pSid(512) As Byte
      Dim pSubAuthorityCount As Long
      Dim bSubAuthorityCount As Byte
      Dim pAuthority As Long
      Dim lAuthority As Long
      '
      ' Get the SID for the user's account, targetting a local PDC
      '
      IReturn = LookupAccountName(Get_Primary_DCName("", strNTDomain), strNTAccount, pSid(0), 512, pDomain, 512, 1)
      '
      ' Convert the raw sid into its SDDL form ( S-?-?-???-?????? )
      ' The first item in the S- format is the rivision level.  If we look closely at the
      ' SID structure in the WinNT.H C Header, we find that the revision value for the SID is
      ' stored in the 0th byte to the raw sid.
      '
      ' Another interesting fact is that the last byte of the Identifying authority structure contains
      ' the second component of the SDDL form, so lets retrieve both of those and
      ' place them into the string.
      '
      pSia = GetSidIdentifierAuthority(pSid(0))
      '
      ' The GetSidIdentifierAuthority returns a pointer to the Identifying Authority structure
      ' The pointer must be copied into some memory that VB knows how to manage, so....
      '
      CopyByValMemory pSiaByte(0), pSia, 6
      SDDL_SID = "S-" + LTrim(str(pSid(0))) + "-" + LTrim(str(pSiaByte(5)))    '
      '
      ' The rest of the SDDL form contains a list of sub authorities separated by
      ' "-"s.  The total number of these authorities can be obtained by
      ' calling the GetSidSubAuthorityCount.  The value returned is a pointer into the
      ' SID memory that contains the Sub Authority value, once again, this memory
      ' must be copied into something that VB knows how to manage.
      '
      ' Notice that only 1 byte is copied.  This is because the sub authority count
      ' is stored in a single byte ( see the SID srtructure definition above )
      '
      pSubAuthorityCount = GetSidSubAuthorityCount(pSid(0))
      CopyByValMemory bSubAuthorityCount, pSubAuthorityCount, 1
      '
      ' We can loop throught the sub authorities and convert
      ' their DWORD values to VB longs, then convert them to a
      ' string.
      ' The count is 0 based, so we start a 0 and goto the
      ' number of sub authorities - 1
      '
      For AuthCount = 0 To bSubAuthorityCount - 1
        pAuthority = GetSidSubAuthority(pSid(0), AuthCount)
        CopyByValMemory lAuthority, pAuthority, LenB(lAuthority)
      '
      ' VB by default assumes its data types to be signed types. The sub authorities should
      ' be unsigned and hence the following code has to work around this problem to get the
      ' sub authorities as unsigned data types
      '
      ' Get rid of the most significant bit (the signed bit) if it is set (And &H7FFFFFFF), and then add it
      ' back in into the right location in the double variable (+ 2^31)
      '
        dAuthority = lAuthority
        If ((lAuthority And &H80000000) <> 0) Then
          dAuthority = lAuthority And &H7FFFFFFF
          dAuthority = dAuthority + 2 ^ 31
        End If
        SDDL_SID = SDDL_SID + "-" + LTrim(Str(dAuthority))

      Next AuthCount
      '
      ' We are done, the SDDL_SID variable contains the SID in
      ' SDDL form,
      ' Return it...
      '
      Convert_BIN_To_SDDL = SDDL_SID
  End Function
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  ''
  '' Get_Primary_DCName -- gets the name of the Primary Domain Controller for
  ''                       the NT domain
  ''
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  Function Get_Primary_DCName(ByVal MName As String, ByVal DName As String) As String

  Dim Result As Long
  Dim DCName As String
  Dim DCNPtr As Long
  Dim DNArray() As Byte
  Dim MNArray() As Byte
  Dim DCNArray(100) As Byte

      MNArray = MName & vbNullChar
      DNArray = DName & vbNullChar
      Result = NetGetDCName(MNArray(0), DNArray(0), DCNPtr)
      If Result <> 0 Then
          Exit Function
      End If
      Result = PtrToStr(DCNArray(0), DCNPtr)
      Result = NetApiBufferFree(DCNPtr)
      DCName = DCNArray()
      Get_Primary_DCName = DCName
      
  End Function
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  ''
  '' Convert_ObjectSID_To_SDDL converts the objectSID property of a user
  '' into the SDDL form of the SID.
  ''
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  Public Function Convert_ObjectSID_To_SDDL(obj As Variant) As String
      Dim SDDL_SID As String
      Dim pSia As Long
      Dim pSiaByte(5) As Byte
      Dim pSid(512) As Byte
      Dim pSubAuthorityCount As Long
      Dim bSubAuthorityCount As Byte
      Dim pAuthority As Long
      Dim lAuthority As Long
      Dim cbpSid As Long
      '
      ' Move the obj array into a byte array to work with the raw SID
      ' Not sure why we need to do this, but if we use the variant passed
      ' as the argument, the GetSidSubAuthorityCount API does not return a valid
      ' address... Curious... It might a typing issue, the obj property is
      ' defined as a variant array ob bytes were as the pSid array is defined as
      ' an array of bytes.
      '
      cbpSid = 0
      For I = LBound(obj) To UBound(obj)
         pSid(cbpSid) = obj(I)
         cbpSid = cbpSid + 1
      Next I
      '
      ' Convert the raw sid into its SDDL form ( S-?-?-???-?????? )
      ' The first item in the S- format is the rivision level.  If we look closely at the
      ' SID structure in the WinNT.H C Header, we find that the revision value for the SID is
      ' stored in the 0th byte to the raw sid.
      '
      ' Another interesting fact is that the last byte of the Identifying authority structure contains
      ' the second component of the SDDL form, so lets retrieve both of those and
      ' place them into the string.
      '
      pSia = GetSidIdentifierAuthority(pSid(0))
      '
      ' The GetSidIdentifierAuthority returns a pointer to the Identifying Authority structure
      ' The pointer must be copied into some memory that VB knows how to manage, so....
      '
      CopyByValMemory pSiaByte(0), pSia, 6
      SDDL_SID = "S-" + LTrim(str(pSid(0))) + "-" + LTrim(str(pSiaByte(5)))    '
      '
      ' The rest of the SDDL form contains a list of sub authorities separated by
      ' "-"s.  The total number of these authorities can be obtained by
      ' calling the GetSidSubAuthorityCount.  The value returned is a pointer into the
      ' SID memory that contains the Sub Authority value, once again, this memory
      ' must be copied into something that VB knows how to manage.
      '
      ' Notice that only 1 byte is copied.  This is because the sub authority count
      ' is stored in a single byte ( see the SID srtructure definition above )
      '
      pSubAuthorityCount = GetSidSubAuthorityCount(pSid(0))
      CopyByValMemory bSubAuthorityCount, pSubAuthorityCount, 1
      '
      ' We can loop throught the sub authorities and convert
      ' their DWORD values to VB longs, then convert them to a
      ' string.
      ' The count is 0 based, so we start a 0 and goto the
      ' number of sub authorities - 1
      '
      For AuthCount = 0 To bSubAuthorityCount - 1
        pAuthority = GetSidSubAuthority(pSid(0), AuthCount)
        CopyByValMemory lAuthority, pAuthority, LenB(lAuthority)
      '
      ' VB by default assumes its data types to be signed types. The sub authorities should
      ' be unsigned and hence the following code has to work around this problem to get the
      ' sub authorities as unsigned data types
      '
      ' Get rid of the most significant bit (the signed bit) if it is set (And &H7FFFFFFF), and then add it
      ' back in into the right location in the double variable (+ 2^31)
      '
        dAuthority = lAuthority
        If ((lAuthority And &H80000000) <> 0) Then
          dAuthority = lAuthority And &H7FFFFFFF
          dAuthority = dAuthority + 2 ^ 31
        End If
        SDDL_SID = SDDL_SID + "-" + LTrim(Str(dAuthority))
      Next AuthCount
      '
      ' We are done, the SDDL_SID variable contains the SID in
      ' SDDL form,
      ' Return it...
      '
      Convert_ObjectSID_To_SDDL = SDDL_SID
      '
  End Function
  '++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  ' Local_Convert_BIN_To_SDDL takes two arguments: strNTDomain-> Netbios name for the NT machine to 
  '                                                              Request account information from
  '                                          strNTAccount-> The NT SAM account name for the user
  ' The function returns the SDDL form of the user's SID,
  '
  Public Function Local_Convert_BIN_To_SDDL(strNTDomain As String, strNTAccount As String) As String ' pSid As pSid, pszSidText)
      Dim SDDL_SID As String
      Dim pSia As Long
      Dim pSiaByte(5) As Byte
      Dim pSid(512) As Byte
      Dim pSubAuthorityCount As Long
      Dim bSubAuthorityCount As Byte
      Dim pAuthority As Long
      Dim lAuthority As Long
      '
      ' Get the SID for the user's account, targetting a local PDC
      '
      IReturn = LookupAccountName(strNTDomain, strNTAccount, pSid(0), 512, pDomain, 512, 1)
      '
      ' Convert the raw sid into its SDDL form ( S-?-?-???-?????? )
      ' The first item in the S- format is the rivision level.  If we look closely at the
      ' SID structure in the WinNT.H C Header, we find that the revision value for the SID is
      ' stored in the 0th byte to the raw sid.
      '
      ' Another interesting fact is that the last byte of the Identifying authority structure contains
      ' the second component of the SDDL form, so lets retrieve both of those and
      ' place them into the string.
      '
      pSia = GetSidIdentifierAuthority(pSid(0))
      '
      ' The GetSidIdentifierAuthority returns a pointer to the Identifying Authority structure
      ' The pointer must be copied into some memory that VB knows how to manage, so....
      '
      CopyByValMemory pSiaByte(0), pSia, 6
      SDDL_SID = "S-" + LTrim(str(pSid(0))) + "-" + LTrim(str(pSiaByte(5)))    '
      '
      ' The rest of the SDDL form contains a list of sub authorities separated by
      ' "-"s.  The total number of these authorities can be obtained by
      ' calling the GetSidSubAuthorityCount.  The value returned is a pointer into the
      ' SID memory that contains the Sub Authority value, once again, this memory
      ' must be copied into something that VB knows how to manage.
      '
      ' Notice that only 1 byte is copied.  This is because the sub authority count
      ' is stored in a single byte ( see the SID srtructure definition above )
      '
      pSubAuthorityCount = GetSidSubAuthorityCount(pSid(0))
      CopyByValMemory bSubAuthorityCount, pSubAuthorityCount, 1
      '
      ' We can loop throught the sub authorities and convert
      ' their DWORD values to VB longs, then convert them to a
      ' string.
      ' The count is 0 based, so we start a 0 and goto the
      ' number of sub authorities - 1
      '
      For AuthCount = 0 To bSubAuthorityCount - 1
        pAuthority = GetSidSubAuthority(pSid(0), AuthCount)
        CopyByValMemory lAuthority, pAuthority, LenB(lAuthority)
      '
      ' VB by default assumes its data types to be signed types. The sub authorities should
      ' be unsigned and hence the following code has to work around this problem to get the
      ' sub authorities as unsigned data types
      '
      ' Get rid of the most significant bit (the signed bit) if it is set (And &H7FFFFFFF), and then add it
      ' back in into the right location in the double variable (+ 2^31)
      '
        dAuthority = lAuthority
        If ((lAuthority And &H80000000) <> 0) Then
          dAuthority = lAuthority And &H7FFFFFFF
          dAuthority = dAuthority + 2 ^ 31
        End If
        SDDL_SID = SDDL_SID + "-" + LTrim(Str(dAuthority))
      Next AuthCount
      '
      ' We are done, the SDDL_SID variable contains the SID in
      ' SDDL form,
      ' Return it...
      '
      Local_Convert_BIN_To_SDDL = SDDL_SID
  End Function

Additional query words:

======================================================================
Keywords          : kbSecurity kbDSupport kb32bitOnly 
Technology        : kbWinNTsearch kbWinNT400search kbwin2000AdvServ kbwin2000AdvServSearch kbwin2000Serv kbWinNTSsearch kbWinNTS400search kbWinNTS400 kbwin2000ServSearch kbwin2000Search kbWinAdvServSearch
Version           : :2000,4.0
Issue type        : kbhowto

=============================================================================

THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Copyright Microsoft Corporation 1986-2002.