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:
[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.
public static string GetSid(string name)
{
IntPtr _sid = IntPtr.Zero;
int _sidLength = 0;
int _domainLength = 0;
int _use;
StringBuilder _domain = new StringBuilder();
int _error = 0;
string _sidString = "";
LookupAccountName(null, name, _sid, ref _sidLength, _domain,
ref _domainLength, out _use);
_error = Marshal.GetLastWin32Error();
if (_error != 122)
{
throw(new Exception(new Win32Exception(_error).Message));
}
else
{
_domain = new StringBuilder(_domainLength);
_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
{
_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.
public static string GetName(string sid)
{
IntPtr _sid = IntPtr.Zero;
int _nameLength = 0;
int _domainLength = 0;
int _use;
StringBuilder _domain = new StringBuilder();
int _error = 0;
StringBuilder _name = new StringBuilder();
bool _rc0 = ConvertStringSidToSid(sid, ref _sid);
if (_rc0 == false)
{
_error = Marshal.GetLastWin32Error();
Marshal.FreeHGlobal(_sid);
throw(new Exception(new Win32Exception(_error).Message));
}
bool _rc = LookupAccountSid(null, _sid, _name, ref _nameLength, _domain,
ref _domainLength, out _use);
_domain = new StringBuilder(_domainLength);
_name = new StringBuilder(_nameLength);
_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.