Click here to Skip to main content
15,860,972 members
Articles / Programming Languages / C#
Article

SID Translator Library

Rate me:
Please Sign up or sign in to vote.
4.43/5 (19 votes)
17 Jan 20051 min read 57K   748   28   6
The article briefly describes the library for conversion of Windows domain account name into its security identifier (SID) and vice versa.

Introduction

SidTranslator is a simple library with only two methods:

  • GetSid, which converts the given MS Windows account name into its security identifier (SID).
  • GetName, which converts the SID into the domain account name.

It has only about one hundred lines of code, but I spent a couple of hours to make it working, so I'd like to share it with others to help them avoid the mistakes I did.

Using the code

I use four methods provided by the unmanaged dynamic-link library advapi32.dll - LookupAccountSid, LookupAccountName, ConvertSidToStringSid and ConvertStringSidToSid. They are declared at the first part of the code:

C#
[DllImport( "advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern bool LookupAccountSid(
    [In,MarshalAs(UnmanagedType.LPTStr)] string systemName,
    IntPtr sid,
    [Out,MarshalAs(UnmanagedType.LPTStr)] StringBuilder name,
    ref int cbName,
    StringBuilder referencedDomainName,
    ref int cbReferencedDomainName,
    out int use );

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public static extern bool LookupAccountName(
    [In,MarshalAs(UnmanagedType.LPTStr)] string systemName,
    [In,MarshalAs(UnmanagedType.LPTStr)] string accountName,
    IntPtr sid,
    ref int cbSid,
    StringBuilder referencedDomainName,
    ref int cbReferencedDomainName,
    out int use);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
internal static extern bool ConvertSidToStringSid(
    IntPtr sid,
    [In,Out,MarshalAs(UnmanagedType.LPTStr)] ref string pStringSid);

[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
internal static extern bool ConvertStringSidToSid(
    [In, MarshalAs(UnmanagedType.LPTStr)] string pStringSid,
    ref IntPtr sid);

The first method, GetSid takes an account name as a parameter and returns the SID in its string form.

C#
public static string GetSid(string name)
{
    IntPtr _sid = IntPtr.Zero;    //pointer to binary form of SID string.
    int _sidLength = 0;            //size of SID buffer.
    int _domainLength = 0;        //size of domain name buffer.
    int _use;                    //type of object.
    //stringBuilder for domain name.
    StringBuilder _domain = new StringBuilder();    
    int _error = 0;                
    string _sidString = "";

    //first call of the function only returns the size 
    //of buffers (SID, domain name)
    LookupAccountName(null, name, _sid, ref _sidLength, _domain, 
                       ref _domainLength, out _use);
    _error = Marshal.GetLastWin32Error();

    //error 122 (The data area passed to a system call is too small) 
    // normal behaviour.
    if (_error != 122) 
    {
        throw(new Exception(new Win32Exception(_error).Message));
    }
    else
    {
        //allocates memory for domain name
        _domain = new StringBuilder(_domainLength); 
         //allocates memory for SID
        _sid = Marshal.AllocHGlobal(_sidLength);   
        bool _rc = LookupAccountName(null, name, _sid, ref _sidLength, _domain, 
                                  ref _domainLength, out _use);

        if (_rc == false)
        {
            _error = Marshal.GetLastWin32Error();
            Marshal.FreeHGlobal(_sid);
            throw(new Exception(new Win32Exception(_error).Message));
        }
        else
        {
            // converts binary SID into string
            _rc = ConvertSidToStringSid(_sid, ref _sidString);

            if (_rc == false)
            {
                _error = Marshal.GetLastWin32Error();
                Marshal.FreeHGlobal(_sid);
                throw(new Exception(new Win32Exception(_error).Message));
            }
            else
            {
                Marshal.FreeHGlobal(_sid);
                return _sidString;
            }
        }
    }

}       

Please notice that the LookupAccountName method is called twice. The first call returns the number of bytes you need to store SID and domain name only. The second call does the rest. At the end of the code, I call ConvertSidToStringSid method which converts binary form of security identifier into the string representation of SID.

The second method, GetName, takes string representation of SID and returns account name.

C#
public static string GetName(string sid)
{
    IntPtr _sid = IntPtr.Zero;    //pointer to binary form of SID string.
    int _nameLength = 0;        //size of object name buffer
    int _domainLength = 0;        //size of domain name buffer
    int _use;                    //type of object
    StringBuilder _domain = new StringBuilder();    //domain name variable
    int _error = 0;
    StringBuilder _name = new StringBuilder();        //object name variable

    //converts SID string into the binary form
    bool _rc0 = ConvertStringSidToSid(sid, ref _sid);    

    if (_rc0 == false)
    {
        _error = Marshal.GetLastWin32Error();
        Marshal.FreeHGlobal(_sid);
        throw(new Exception(new Win32Exception(_error).Message));
    }

    //first call of method returns the size of domain name 
    //and object name buffers
    bool _rc = LookupAccountSid(null, _sid, _name, ref _nameLength, _domain, 
                     ref _domainLength, out _use);
    _domain = new StringBuilder(_domainLength);    //allocates memory for domain name
    _name = new StringBuilder(_nameLength);        //allocates memory for object name
    _rc = LookupAccountSid(null, _sid,  _name, ref _nameLength, _domain, 
                     ref _domainLength, out _use);

    if (_rc == false)
    {
        _error = Marshal.GetLastWin32Error();
        Marshal.FreeHGlobal(_sid);
        throw(new Exception(new Win32Exception(_error).Message));
    }
    else
    {
        Marshal.FreeHGlobal(_sid);
        return _domain.ToString() + "\\" + _name.ToString();
    }
}

As the first step it's necessary to convert string SID into its binary form using the ConvertStringSidToSid method. LookupAccountSid method does the rest. Again, it's necessary to call it twice, as the first call returns the size of buffers only.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here



Comments and Discussions

 
QuestionError Pin
Member 294550724-Mar-09 8:26
Member 294550724-Mar-09 8:26 
AnswerRe: Error Pin
Martin Budař24-Mar-09 9:17
Martin Budař24-Mar-09 9:17 
GeneralMemory Leakage Pin
Member 1196423-May-05 3:39
Member 1196423-May-05 3:39 
GeneralP/Invoke bool Pin
Anders Dalvander17-Jan-05 1:33
Anders Dalvander17-Jan-05 1:33 
You should be careful with the returning of bools using P/Invoke. It should be treated as an Int32 where zero is false and not zero is true.
GeneralRe: P/Invoke bool Pin
Martin Budar17-Jan-05 4:14
Martin Budar17-Jan-05 4:14 
GeneralRe: P/Invoke bool Pin
wmhp114-Apr-07 7:20
wmhp114-Apr-07 7:20 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.