Looking Up an Account SID Example

Sometimes you know the name of a well-known account, such as Administrators, but you don't know anything else about it. The Win32 API provides an answer to this dilemma. You can create a SID for the account without knowing anything about it and then look up the information for that account. This technique proves handy for a number of uses. For example, if you know that you want to create a new user that has starting rights that are the same as those used for a well-known account, you can begin by obtaining information about the well-known account SID. Listing 8.1 shows the code you'll need for this example. You'll find the source code for this example in the \Chapter 08\C#\LookUpSID and \Chapter 08\VB\LookUpSID folders of the CD. (Note that the list of well-known SIDs shown in Listing 8.1 is incomplete—you'll find a complete list on the CD.)

Listing 8.1: Converting a SID to Human-Readable Form

// This function returns a SID for a well-known account. [DllImport("AdvAPI3 2.DLL", CharSet=CharSet.Auto, SetLastError=true )] public static extern bool CreateWellKnownSid( WELL_KNOWN_SID_TYPE WellKnownSidType, IntPtr DomainSid, IntPtr pSid, ref Int32 cbSid);

// This enumeration contains a list of the well-known SIDs.

public enum WELL_KNOWN_SID_TYPE {

WinNullSid = 0,

// Lots of other well-known SIDs appear in the source code.

WinAccountRasAndIasServersSid = 50,

// This define is normally calculated by a macro, but it's // unlikely to change for either Windows 2000 or Windows XP. public const int SECURITY_MAX_SID_SIZE = 68;

// This function accepts a SID as input and obtains human // readable data about it.

[DllImport("AdvAPI3 2.DLL", CharSet=CharSet.Auto, SetLastError=true )] public static extern bool LookupAccountSid( String lpSystemName, IntPtr Sid, StringBuilder Name, ref Int32 cbName,

StringBuilder DomainName, ref Int32 cbDomainName, ref SID_NAME_USE peUse);

// This enumeration determines the use of the account.

public enum SID_NAME_USE

SidTypeUser = 1,

SidTypeGroup,

SidTypeDomain,

SidTypeAlias,

SidTypeWellKnownGroup,

SidTypeDeletedAccount,

SidTypelnvalid,

SidTypeUnknown,

SidTypeComputer

private void btnTest_Click(object sender, System.EventArgs e) {

Int32

IntPtr int

Int32

Int32

StringBuilder StringBuilder SID NAME USE

SIDSize; // Size of the returned SID.

GuestSID; // SID of the Guest account.

LastError; // Last error produced by an API call.

NameSize; // Size of the account name.

DomainSize; // Size of the domain name.

// Allocate memory for the SID.

GuestSID = Marshal.AllocHGlobal(SECURITY_MAX_SID_SIZE);

SIDSize = SECURITY_MAX_SID_SIZE;

if (!CreateWellKnownSid((WELL_KNOWN_SID_TYPE)cbSelect.SelectedIndex,

IntPtr.Zero, GuestSID, ref SIDSize))

LastError = Marshal.GetLastWin32Error();

// Display an error message and exit if not successful. MessageBox.Show("Error creating the account SID." +

"\r\nLast Error: " + LastError.ToString() "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

// Free the memory we allocated. Marshal.FreeHGlobal(GuestSID);

// Exit the routine. return;

// Obtain the size of the Name and Domain strings. NameSize = 0; DomainSize = 0;

Use = SID_NAME_USE.SidTypeAlias;

LookupAccountSid(null,

GuestSID, null, ref NameSize, null, ref DomainSize, ref Use);

// Allocate memory for the strings.

Name = new StringBuilder(NameSize);

Domain = new StringBuilder(DomainSize);

// Obtain the SID information.

if (!LookupAccountSid(null,

GuestSID, Name, ref NameSize, Domain, ref DomainSize, ref Use))

LastError = Marshal.GetLastWin32Error();

// Display an error message and exit if not successful.

MessageBox.Show("Error obtaining the account SID data." + "\r\nLast Error: " + LastError.ToString(), "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

else

// Display the account information.

MessageBox.Show("Obtained the SID Account Information" + "\r\nName: " + Name.ToString() + "\r\nDomain: " + Domain.ToString() + "\r\nUse: " + Use.ToString(), "Application Output", MessageBoxButtons.OK, MessageBoxIcon.Information);

// Free the memory we allocated.

Marshal.FreeHGlobal(GuestSID);

Windows provides a wealth of well-known SIDs—predefined SIDs that every machine can use. The CreateWellKnownSid() function will create a SID for a well-known value such as the World. All you need to supply is an enumerated SID type, a pointer to a buffer to hold the SID, and the size of the SID buffer. The domain SID is optional. However, supplying this value will enable you to look up SIDs on other machines. There are 51 enumerated SID types to choose from and the example application lets you test them all. (Some of the well-known SIDs might not work on your machine if you don't have the required support installed.)

The LookupAccountSid() function accepts a SID as input. It doesn't matter where you get the SID as long as the SID is valid. If the call to this function fails, you can assume the SID was invalid—even if it's a well-known SID. In some cases, this function can tell you which operating system features are installed because some security accounts are only installed when you install the appropriate operating system feature. The LookupAccountSid() function returns the name and domain information for the SID along with the SID usage as indicated by the SID_NAME_USE enumeration.

One of the first tasks the code has to perform is allocating memory for the SID. In many cases, the code could allocate local memory as shown in the Platform SDK documentation. However, when working with a .NET application, it's best to use the Marshal.AllocHGlobal() function. This function returns an IntPtr to the allocated memory, which you must deallocate later using the Marshal.FreeHGlobal() function. The SECURITY_MAX_SID_SIZE constant defines the maximum size of the SID. This is yet another instance where you can convert a Visual C++ macro into a constant with the caveat that Microsoft could change the size of a SID at some later date. The alternative, in this case, is to write a small wrapper DLL to calculate the value for you. Using this technique is more expensive in development time, but it does protect you from future changes.

We've used a number of techniques for gaining access to error information. This example uses the Microsoft-recommended technique of setting the SetLastError argument of the [DllImport] attribute true and then using the Marshal.GetLastWin32Error() function to return the error number. Note that the .NET Framework doesn't provide any means for converting this number into a human-readable form. You still need to use the Win32 API FormatMessage() function to perform the conversion. (See the section "Interpreting Error and Result Values" in Chapter 3 for details.)

Once the code obtains the desired SID, it uses the LookupAccountSid() function to determine the SID information. However, the code requires two calls to the LookupAccountSid() function to perform this task. The first call returns the size of the strings used to contain the account name and domain information. The code uses this information to allocate two StringBuilder variables. The second call returns the actual information. Figure 8.6 shows typical output from this example for the WinAnonymousSid enumerated value.

Was this article helpful?

0 0

Responses

  • REN DIEDERICH
    How to find windows account sid size?
    8 years ago

Post a comment