我有一个类库,保存系统范围的configuration数据在registry中(HKLM \ Software \ XXX)。 这个库被用于各种版本的Windows(XP,2003,7,2008 R2)中的各种应用程序(服务,窗体,Web应用程序,控制台应用程序)。 因此,应用程序的身份不一致,甚至可能不是本机pipe理员组的成员。 所以我创build了一个AD域pipe理员用户,并进行模拟以获得对registry的写入权限。 这在XP / 2003中完美工作,但在UAC系统(7 / 2008R2)中不能。 我的理解是,只有交互式login将令牌拆分,这意味着非交互式login(服务标识,应用程序池标识等)不会。 我找不到任何证实,但从这个假设工作,我所做的冒充应该工作。
我编写了一个包装类,使用本机LogonUser(networkinglogintypes,默认提供程序)和DuplicateTokenEx(模拟,主令牌),然后使用WindowsIdentity.Impersonate()进行模拟。 我得到一个对我的根键的引用:
using (ECR.Impersonator imp = new ECR.Impersonator("XXX", "XXX", "XXX")) { _root = Registry.LocalMachine.CreateSubKey("SOFTWARE\\XXX", RegistryKeyPermissionCheck.ReadWriteSubTree); }
根据MSDN ,通过使用ReadWriteSubTree,这应该是安全检查完成的唯一时间。 我可以将值写入该键,创build子键(也使用ReadWriteSubTree)并将值写入这些子键,而不需要另外的安全检查。 所以我认为我只需要做一次昂贵的模仿 – 获得对我的根键的引用。
我可以写我的根键的值就好了:
_root.SetValue("cachedDate", value.ToBinary(), RegistryValueKind.QWord); }
但是当我用ReadWriteSubTree创build/打开一个子键时:
RegistryKey key = _root.CreateSubKey("XXX", RegistryKeyPermissionCheck.ReadWriteSubTree);
它炸弹与Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\XXX\XXX' is denied
。
虽然我很好奇为什么安全检查完成时,MSDN说,它不应该,我的问题是我怎样才能通过模拟的应用程序可能不会在交互式login下运行提升权限?
它提出了LogonUser()只返回受限令牌。 搜索确认,我得到的印象是LogonUser()返回限制令牌只为interative会议。 我创建了一些测试来了解。
第一个是控制台应用程序:
using (ECR.Impersonator imp = new ECR.Impersonator("XXX", "XXX", "XXX")) { WindowsIdentity ident = WindowsIdentity.GetCurrent(); WindowsPrincipal princ = new WindowsPrincipal(ident); Console.WriteLine("{0}, {1}", ident.Name, princ.IsInRole(WindowsBuiltInRole.Administrator)); RegistryKey root = Registry.LocalMachine.CreateSubKey("SOFTWARE\\Connection Strings", RegistryKeyPermissionCheck.ReadWriteSubTree); RegistryKey key = root.CreateSubKey("AbacBill", RegistryKeyPermissionCheck.ReadWriteSubTree); }
当在提升的控制台中运行时,IsInRole()返回true,并且没有错误打开子项。 当在非提升控制台中运行时,IsInRole()返回true和errored打开子项:
Unhandled Exception: System.IO.IOException: Unknown error "1346". at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str) at Microsoft.Win32.RegistryKey.CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistrySecurity registrySecurity) at test.Program.test14() at test.Program.Main(String[] args)
所以看起来,在非提升的交互式会话中,LogonUser()的确会返回受限令牌。 有趣的是,做IsInRole()的正常测试意外地返回true。
第二个测试是一个网站。 我把相同的代码(取代Console.Write与literal1.Text = string.Format):IsInRole()返回true,没有错误打开子项,IIS7.5:匿名身份验证,应用程序池:经典的管道,ApplicationPoolIdentity,2.0框架, web.config:身份验证模式=无,没有模拟。
所以这似乎证实了我的印象,LogonUser()仅为交互式会话返回受限令牌,但非交互式会话获取完整令牌。
做这些测试帮助我回答我自己的问题。 我的类库主要用于Web应用程序,并且在应用配置更新(访问拒绝打开子项)时始终弹出。 所以我改变我的测试,以更准确地反映我在做什么(冒充只是为了得到我的根密钥的参考):
protected void Page_Load(object sender, EventArgs e) { RegistryKey root = null; using (ECR.Impersonator imp = new ECR.Impersonator("XXX", "XXX", "XXX")) { WindowsIdentity ident = WindowsIdentity.GetCurrent(); WindowsPrincipal princ = new WindowsPrincipal(ident); lit.Text = string.Format("{0}, {1}", ident.Name, princ.IsInRole(WindowsBuiltInRole.Administrator)); root = Registry.LocalMachine.CreateSubKey("SOFTWARE\\XXX", RegistryKeyPermissionCheck.ReadWriteSubTree); } root.SetValue("test", "test"); RegistryKey key = root.CreateSubKey("XXX", RegistryKeyPermissionCheck.ReadWriteSubTree); }
它错误:
[UnauthorizedAccessException: Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\XXX\XXX' is denied.] Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str) +3803431 Microsoft.Win32.RegistryKey.CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistrySecurity registrySecurity) +743 webtest.impTest.Page_Load(Object sender, EventArgs e) in D:\VS 2008 Projects\test\webtest\impTest.aspx.cs:28 System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +25 System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +42 System.Web.UI.Control.OnLoad(EventArgs e) +132 System.Web.UI.Control.LoadRecursive() +66 System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +2428
同样,没有问题写入我的根键值,只是打开子项。 所以看起来,使用RegistryKeyPermissionCheck.ReadWriteSubTree确实没有进行任何进一步的安全检查时,写入该密钥,但IS打开一个子键时,即使与RegistryKeyPermissionCheck.ReadWriteSubTree(尽管文档说不)进行另一个安全检查。
我的问题的答案是,它通过在非交互式登录下通过模拟适当地给予提升的权限。 我的问题是我假设RegistryKeyPermissionCheck.ReadWriteSubTree不会进行任何进一步的安全检查(如文档说)即使在模拟结束后的引用。
我想我每次需要写入注册表时都必须进行模拟操作。 🙁
我不明白微软如何声称不会进行安全检查。 我简要地看了一下代码,这些方法并不比原生RegOpenKeyEx / RegCreateKeyEx函数的包装更多,所以显然会进行安全检查。 RegistryKeyPermissionCheck似乎控制在P /调用本机函数之前,RegistryKey类本身是否自己进行内部安全检查。 所以你可以传递你想要的任何标志到这个RegistryKey类,但是如果底层的注册表键没有授予你访问权,那么你将得到一个拒绝访问的异常。
至于LogonUser(),从我读过的看来,它总是会给你受限的令牌。