我正在尝试将具有安全规则的所有用户提取到某个文件夹,为此我正在使用C#应用程序,但是我无法为用户获取正确的域。
假设我在域CURRENT上,但有些访问该文件夹的用户也在域OLD上。在域之间有一个信任。但是,当我查找其SID时,由于某种原因从OLD迁移到CURRENT的某些用户似乎在CURRENT上。然而奇怪的是,Windows资源管理器正确地显示它们在域OLD上。
public IdentityReference GetUser(string SID)
{
return ( new NTAccount(SID) ).Translate(typeof(NTAccount));
}
如您所见,它获取用户的SID并返回NTAccount(DOMAIN \ user)。要获取SID,我使用以下代码:
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 ...
}
我还有一个PowerShell脚本可以执行相同的操作:
echo (New-Object System.Security.Principal.SecurityIdentifier($sid)).Translate([System.Security.Principal.NTAccount]).Value
现在,我有用户OLD \ user1,但是,它也作为CURRENT \ user1出现在新域中。当我右键单击Windows资源管理器时,它正确地将用户显示为OLD \ user1,但是当我运行C#代码时,它总是给我CURRENT \ user1,这不是我想要的。
奇怪的是,当我尝试在PowerShell中执行相同的操作时,用户也被正确识别为OLD \ user1。更奇怪的是,如果我运行PowerShell脚本并且只运行C#代码,我会得到正确的结果,但是在10分钟后,C#代码再次出现错误。
鉴于这些信息,我发现它必定是一种缓存问题(因为它似乎也发生在其他人身上)并且由于某种原因,C#和PowerShell以不同的方式处理查找请求,(见下面的 EDIT1 )所以我尝试了以下内容:
从C#内部运行PowerShell代码
PowerShell ps = PowerShell.Create();
ps.AddScript("New-Object System.Security.Principal.SecurityIdentifier(\""+SID+"\")).Translate([System.Security.Principal.NTAccount]");
return (IdentityReference)(ps.Invoke()[0].BaseObject);
运行与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));
使用P / Invoke使用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()));
但这些都没有解决这个问题。还请注意,更改服务器缓存选项并不是真正的解决方案,因为我无法做到这一点。
显然,PowerShell以不同的方式处理请求是不正确的,我忘了提到的是,在PS代码中,在从SID请求NTAccount之前,我做了相反的反应:我首先请求了SID NTAccount,像这样:
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
这一定必须触发缓存,所以它绝对是一个缓存问题。
我已经设法通过将可执行文件移动到OLD域下的计算机来解决问题(因为这是它应该从另一台机器远程执行其任务的地方),但我将离开问题没有解决,因为它不是真正最好的解决方案。