How can I convert from a SID to an account name in C#
Asked Answered
A

11

61

I have a C# application that scans a directory and gathers some information. I would like to display the account name for each file. I can do this on the local system by getting the SID for the FileInfo object, and then doing:

string GetNameFromSID( SecurityIdentifier sid )
{
    NTAccount ntAccount = (NTAccount)sid.Translate( typeof( NTAccount ) );
    return ntAccount.ToString();
}

However, this does not work for files on a network, presumably because the Translate() function only works with local user accounts. I thought maybe I could do an LDAP lookup on the SID, so I tried the following:

string GetNameFromSID( SecurityIdentifier sid )
{
    string str = "LDAP://<SID=" + sid.Value + ">";
    DirectoryEntry dirEntry = new DirectoryEntry( str );
    return dirEntry.Name;
}

This seems like it will work, in that the access to "dirEntry.Name" hangs for a few seconds, as if it is going off and querying the network, but then it throws a System.Runtime.InteropServices.COMException

Does anyone know how I can get the account name of an arbitrary file or SID? I don't know much about networking or LDAP or anything. There's a class called DirectorySearcher that maybe I'm supposed to use, but it wants a domain name, and I don't know how to get that either - all I have is the path to the directory I'm scanning.

Albarran answered 31/1, 2009 at 15:56 Comment(3)
Is there a specific message that the COMException shows?Visional
It could be group policy an allowing you to traverse the directory service locally, that's why you are getting the COM exception. What does the error message shows? Try running filemon on the network machine why you are accessing it and see the results.Elam
I meant "group policy not allowing..."Elam
M
21

The SecurityReference object's Translate method does work on non-local SIDs but only for domain accounts. For accounts local to another machine or in a non-domain setup you would need to PInvoke the function LookupAccountSid specifying the specific machine name on which the look up needs to be performed.

Mackenziemackerel answered 1/2, 2009 at 23:24 Comment(2)
I've just started writing some code to do something similar and found this to be correct. P/Invoking LookupAccountSid is better than using SecurityIdentifier.Translate(). I'm working in a multi-forest/domain environment with 60k+ users.Pectin
The Translatemethod returns pre-Windows 2000 names for groups. Instead of PInvoke you can also use the FindByIdentity method oonSystem.DirectoryServices.AccountManagement.GroupPrincipal.Manufacturer
C
55

See here for a good answer:

The best way to resolve display username by SID?

The gist of it is this bit:

string sid="S-1-5-21-789336058-507921405-854245398-9938";
string account = new System.Security.Principal.SecurityIdentifier(sid).Translate(typeof(System.Security.Principal.NTAccount)).ToString();

This approach works for me for non-local SID's over the active directory.

Capable answered 11/3, 2009 at 23:36 Comment(0)
M
21

The SecurityReference object's Translate method does work on non-local SIDs but only for domain accounts. For accounts local to another machine or in a non-domain setup you would need to PInvoke the function LookupAccountSid specifying the specific machine name on which the look up needs to be performed.

Mackenziemackerel answered 1/2, 2009 at 23:24 Comment(2)
I've just started writing some code to do something similar and found this to be correct. P/Invoking LookupAccountSid is better than using SecurityIdentifier.Translate(). I'm working in a multi-forest/domain environment with 60k+ users.Pectin
The Translatemethod returns pre-Windows 2000 names for groups. Instead of PInvoke you can also use the FindByIdentity method oonSystem.DirectoryServices.AccountManagement.GroupPrincipal.Manufacturer
D
7

System.DirectoryServices.AccountManagement.UserPrincipal class (msdn link) has a static function FindByIdentity to convert an SID to a User object. It should be able to work both against the local machine or an LDAP/Active Directory server. I have only used it against active directory.

Here is an example that I have used in IIS:

// Set the search context to a specific domain in active directory
var searchContext = new PrincipalContext(ContextType.Domain, "YOURDOMAIN", "OU=SomeOU,DC=YourCompany,DC=com");
// get the currently logged in user from IIS
MembershipUser aspUser = Membership.GetUser();
// get the SID of the user (stored in the SecurityIdentifier class)
var sid = aspUser.ProviderUserKey as System.Security.Principal.SecurityIdentifier;
// get the ActiveDirectory user object using the SID (sid.Value returns the SID in string form)
var adUser = UserPrincipal.FindByIdentity(searchContext, IdentityType.Sid, sid.Value);
// do stuff to user, look up group membership, etc.
Dactylic answered 19/7, 2012 at 23:42 Comment(1)
This is the proper way to query foreign domains. P/Invoke isn't required.Clop
L
3

In C#, get the user SID and assign it to a string variable through:

string strUser = System.Security.Principal.WindowsIdentity.GetCurrent().User.ToString();

You will need to use string because the ability to resolve to the UserName supports string. In other words, using var varUser will result in a namespace error.

string strUserName = new System.Security.Principal.SecurityIdentifier(strUser).Translate(typeof(System.Security.Principal.NTAccount)).ToString();
Lewak answered 19/2, 2017 at 7:33 Comment(3)
This caused the problem = "The trust relationship between this workstation and the primary domain failed"Apodosis
@Apodosis What troubleshooting did you following? Did you run VS in Administrator mode? Did you check the MSDN for this error: support.microsoft.com/en-us/help/2771040/… ?Lewak
Forget about it, I resolved this problem by rejoining the domain.. painful... but it worked!Apodosis
G
2

You can also get account name of special accounts like "Everyone" with code like this that will work regardless of user's language settings:

   SecurityIdentifier everyoneSid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
   string everyone = everyoneSid.Translate(typeof(System.Security.Principal.NTAccount)).ToString();
Grumble answered 1/12, 2015 at 5:34 Comment(1)
Tks. This was a life saver for me :)Nathan
C
1

Ooh, then it's possible that the LDAP call is not working because you might not be in an Active Directory environment. If this is the case, then each of your machines is responsible for its own identity store. And your first code sample is not working across the network because the machine on which you are executing your code does not know how to resolve the SID that only makes sense on the remote machine.

You really should check if your machines are a part of an Active Directory. You would know this during the logon process. Or you can check by right clicking on "My Computer", select "Properties", the "Computer Name" tab, then see if your computer is part of a domain.

Chazan answered 1/2, 2009 at 5:50 Comment(0)
A
1

Great. I cribbed some LookupAccountSid() code from here:

http://www.pinvoke.net/default.aspx/advapi32.LookupAccountSid

And that worked, though I had to provide the host name myself. In the case of a UNC path I can just take the first component of it. When it's a mapped drive, I use this code to convert the path to a UNC one:

http://www.wiredprairie.us/blog/index.php/archives/22

It seems to work, so that's how I'll do it, unless someone comes up with a situation in which the first component of a UNC path isn't the host name...

Thank you all for your help.

Albarran answered 2/2, 2009 at 2:15 Comment(0)
C
0

This one is a stumper. You are in an Active Directory environment right? Just checking:)

Anyhow, instead of binding with sid.Value,

string str = "LDAP://<SID=" + sid.Value + ">";

I would try converting the SID's byte array to an Octet String and bind with that instead.

There is a sweet example here on page 78. This will get you closer. To be honest, I've not tried binding with a SID before. But I've had success binding with a user's GUID though :)

Good luck and let me know how it goes.

Chazan answered 31/1, 2009 at 19:8 Comment(0)
M
0

Get the current domain:

System.DirectoryServices.ActiveDirectory.Domain.GetCurrentDomain();

Get a directory entry from ldap and the domain name:

DirectoryEntry de = new DirectoryEntry(string.Format("LDAP://{0}", domain));

Get the sid from an ActiveDirectoryMembershipProvider ActiveDirectoryMembershipUser:

ActiveDirectoryMembershipUser user = (ActiveDirectoryMembershipUser)Membership.GetUser();
var sid = (SecurityIdentifier)user.ProviderUserKey;

Get the username from the SecurityIdentifier:

(NTAccount)sid.Translate(typeof(NTAccount));

Get directory search done on an activedirectory with the domain directory entry and username:

DirectorySearcher search = new DirectorySearcher(entry);
        search.Filter = string.Format("(SAMAccountName={0})", username);
        search.PropertiesToLoad.Add("Name");
        search.PropertiesToLoad.Add("displayName");
        search.PropertiesToLoad.Add("company");
        search.PropertiesToLoad.Add("homePhone");
        search.PropertiesToLoad.Add("mail");
        search.PropertiesToLoad.Add("givenName");
        search.PropertiesToLoad.Add("lastLogon");
        search.PropertiesToLoad.Add("userPrincipalName");
        search.PropertiesToLoad.Add("st");
        search.PropertiesToLoad.Add("sn");
        search.PropertiesToLoad.Add("telephoneNumber");
        search.PropertiesToLoad.Add("postalCode");
        SearchResult result = search.FindOne();
        if (result != null)
        {
            foreach (string key in result.Properties.PropertyNames)
            {
                // Each property contains a collection of its own
                // that may contain multiple values
                foreach (Object propValue in result.Properties[key])
                {
                    outputString += key + " = " + propValue + ".<br/>";
                }
            }
        }

Depending on the data in your active directory, you will get a varied response in the output.

Here is a site that has all the user properties I needed:

Meniscus answered 17/5, 2009 at 22:27 Comment(0)
F
0

For all the Windows developers, the answer is LookupAccountSid

LookupAccountSid(null, Sid, username, userSize, domainName, domainSize, sidType);
Ferity answered 4/4, 2022 at 16:50 Comment(0)
S
-4

I am quite sure you will be able to use the accepted answer from here: Determine the LocalSystem account name using C#

Basically, you can translate an instance of the SecurityIdentifier class to type NTAccount, from which you can get the user name. In code:

using System.Security.Principal;

SecurityIdentifier sid = new SecurityIdentifier("S-1-5-18");
NTAccount acct = (NTAccount)sid.Translate(typeof(NTAccount));
Console.WriteLine(acct.Value);
Swetlana answered 1/2, 2009 at 10:2 Comment(1)
While this is not incorrect, it just repeats the code that the poster has already pointed out does not work in his situation.Mackenziemackerel

© 2022 - 2024 — McMap. All rights reserved.