C # cannot programmatically get user domain given SID

I am trying to fetch all users who have a security rule for a specific folder, I am using a C # application for this, however I am having trouble getting the correct domain for the user.

Let's say that I am in the CURRENT domain, but some of the users who have access to this folder are also in the OLD domain. There is trust between the domain. However, some users migrated from OLD to CURRENT for whatever reason seem to be on CURRENT when I look at their SIDs. The weird thing, however, is that Windows Explorer correctly shows they are in the OLD domain.

Code

public IdentityReference GetUser(string SID)
{
    return ( new NTAccount(SID) ).Translate(typeof(NTAccount));
}

      

As you can see, it gets the SID of the user and returns NTAccount (DOMAIN \ user). To get the SID, I use the following code:

DirectorySecurity ACL = Directory.GetAccessControl(folder);
AuthorizationRuleCollection rules = ACL.GetAccessRules(true, true, typeof(SecurityIdentifier));

foreach (FileSystemAccessRule rule in rules)
{
    IdentityReference user = GetUser(rule.IdentityReference.value);
    //do stuff ...
}

      

I also have a PowerShell script that does the exact same thing:

echo (New-Object System.Security.Principal.SecurityIdentifier($sid)).Translate([System.Security.Principal.NTAccount]).Value

      

Problem

I now have a user OLD \ user1, which however is also present in the new domain as CURRENT \ user1. When I right click on Windows Explorer it correctly shows me the user as OLD \ user1, but when I run the C # code it always gives me CURRENT \ user1, which is not what I want.

Oddly enough, when I try to do the same in PowerShell, the user is also correctly identified as OLD \ user1. Even weirder, if I run the PowerShell script and only then I run the C # code, I get the correct result, however exactly 10 minutes later the C # code is wrong again.

What i have already tried

Given this information, I figured it must be some sort of caching issue (as it seems to be with other people as well ) , and that for some reason C # and PowerShell handle the search request in a different way, (see EDIT1 below ). so i tried the following:

  • Running PowerShell code from inside C #

    PowerShell ps = PowerShell.Create();
    ps.AddScript("New-Object System.Security.Principal.SecurityIdentifier(\""+SID+"\")).Translate([System.Security.Principal.NTAccount]");
    return (IdentityReference)(ps.Invoke()[0].BaseObject);
    
          

  • Run the same script saved in the same folder as the executable from exe

    Process p = new Process();
    p.StartInfo.FileName = @"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe";
    p.StartInfo.Arguments = String.Format("-File \"{0}\" {1}", Directory.GetCurrentDirectory() + @"\get_user.ps1", SID);
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.Start();
    
    p.WaitForExit();
    return (new NTAccount(output)).Translate(typeof(NTAccount));
    
          

  • Using P / Invoke to Search with WIN32API

    enum SID_NAME_USE
    {
        SidTypeUser = 1,
        SidTypeGroup,
        SidTypeDomain,
        SidTypeAlias,
        SidTypeWellKnownGroup,
        SidTypeDeletedAccount,
        SidTypeInvalid,
        SidTypeUnknown,
        SidTypeComputer
    }
    
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern bool LookupAccountSid(
                          string lpSystemName,
                          [MarshalAs(UnmanagedType.LPArray)] byte[] Sid,
                          StringBuilder lpName,
                          ref uint cchName,
                          StringBuilder ReferencedDomainName,
                          ref uint cchReferencedDomainName,
                          out SID_NAME_USE peUse);
    
    byte[] sid = { /* the SID in bytes */ };
    StringBuilder name = new StringBuilder();
    uint cchName = (uint)name.Capacity;
    StringBuilder referencedDomainName = new StringBuilder();
    uint cchReferencedDomainName = (uint)referencedDomainName.Capacity;
    SID_NAME_USE sid_use;
    
    LookupAccountSid(null, sid, name, ref cchName, referencedDomainName, ref cchReferencedDomainName, out sid_use);
    
    if (Marshal.GetLastWin32Error() == 0)
        Console.WriteLine(String.Format("Account({0}): {2}\\{3}", sid_use, "...", referencedDomainName.ToString(), name.ToString()));
    
          

But none of them solved the problem. Also note that changing the server caching option is not really a solution as I cannot do it.

EDIT1:

Apparently it's not true that PowerShell handles the request differently, I forgot to mention that in the PS code, before asking for the NTAccount from the SID, I did the opposite: I first requested the SID from the NTAccount, for example:

echo (New-Object System.Security.Principal.NTAccount("OLD\user1")).Translate([System.Security.Principal.SecurityIdentifier]).Value
echo (New-Object System.Security.Principal.SecurityIdentifier($sid)).Translate([System.Security.Principal.NTAccount]).Value

      

And that should have caused the cache, so it is definitely a caching issue.

EDIT2

I managed to "solve" the problem by moving the executable file to a computer in the OLD domain (since this is the place where it should perform its task remotely from another computer), but I will leave the issue unresolved, since this is not the best solution.

+3


source to share





All Articles