Как я могу получить повышенные разрешения (UAC) через олицетворение под неинтерактивным входом в систему? - PullRequest
10 голосов
/ 24 февраля 2011

У меня есть библиотека классов, которая хранит общесистемные данные конфигурации в реестре (HKLM \ Software \ XXX). Эта библиотека используется в различных приложениях (сервисы, формы Windows, веб-приложения, консольные приложения) в различных версиях Windows (XP, 2003, 7, 2008 R2). Из-за этого идентификация приложения не согласована и может даже не входить в группу администраторов компьютера. Итак, я создал пользователя-администратора домена AD и выполняю олицетворение, чтобы получить доступ для записи в реестр. Это прекрасно работает в XP / 2003, но не в системах с поддержкой UAC (7 / 2008R2). Насколько я понимаю, только интерактивные учетные записи разделяют токены, что подразумевает, что неинтерактивные учетные записи (идентификаторы служб, идентификаторы пула приложений и т. Д.) Не разделяются. Я не могу найти ничего, чтобы это подтвердить, но исходя из этого предположения, подражание, которое я делаю, должно сработать.

Я написал класс-оболочку для выполнения олицетворения с использованием собственного LogonUser (сетевой тип входа, поставщик по умолчанию) и DuplicateTokenEx (олицетворение, основной токен), а затем WindowsIdentity.Impersonate (). Я получаю ссылку на мой корневой ключ:

using (ECR.Impersonator imp = new ECR.Impersonator("XXX", "XXX", "XXX"))
{
    _root = Registry.LocalMachine.CreateSubKey("SOFTWARE\\XXX", RegistryKeyPermissionCheck.ReadWriteSubTree);
}

Согласно MSDN , с использованием ReadWriteSubTree, это должно быть ТОЛЬКО в тот момент, когда выполняется проверка безопасности. Я могу записывать значения в этот ключ, создавать вложенные ключи (также используя ReadWriteSubTree) и записывать значения в эти вложенные ключи без необходимости дополнительной проверки безопасности. Поэтому я подумал, что мне нужно будет только один раз выполнить дорогостоящую имитацию - получить ссылку на мой корневой ключ.

Я могу записать значения в корневой ключ просто:

_root.SetValue("cachedDate", value.ToBinary(), RegistryValueKind.QWord); }

но когда я создаю / открываю подраздел с помощью ReadWriteSubTree:

RegistryKey key = _root.CreateSubKey("XXX", RegistryKeyPermissionCheck.ReadWriteSubTree);

это бомбы с Access to the registry key 'HKEY_LOCAL_MACHINE\SOFTWARE\XXX\XXX' is denied.

Хотя мне любопытно, почему проверка безопасности выполняется, когда MSDN говорит, что этого не следует делать, у меня вопрос: как я могу получить повышенные разрешения с помощью олицетворения для приложений, которые могут не работать под интерактивным входом в систему?

Ответы [ 2 ]

11 голосов
/ 02 марта 2011

Было установлено, что LogonUser () возвращает только ограниченный токен. В поисках подтверждения у меня сложилось впечатление, что LogonUser () возвращает ограниченный токен только для интерактивных сеансов. Я создал пару тестов, чтобы выяснить это.

Первое - консольное приложение:

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 и допустила ошибку, открыв подраздел:

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 () возвращает ограниченный токен только для интерактивных сеансов, но неинтерактивные сеансы получают полный токен.

Выполнение этих тестов помогло мне ответить на мой собственный вопрос. Моя библиотека классов в основном используется в веб-приложениях, и они постоянно бомбят при применении обновлений конфигурации (доступ запрещен при открытии подключа). Поэтому я изменил свой тест, чтобы более точно отражать то, что я делаю (подражая только для получения ссылки на мой корневой ключ):

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 действительно не выполняет никаких дополнительных проверок безопасности при записи в этот ключ, но выполняет другую проверку безопасности при открытии подключа, даже с RegistryKeyPermissionCheck.ReadWriteSubTree (хотя в документах говорится, что это не так).

Ответ на мой вопрос заключается в том, что он надлежащим образом предоставляет повышенные разрешения посредством олицетворения при неинтерактивном входе в систему. Моя проблема в том, что я предположил, что RegistryKeyPermissionCheck.ReadWriteSubTree не будет выполнять никаких дополнительных проверок безопасности (как говорят в документации) для этой ссылки даже после окончания олицетворения.

Полагаю, мне придется делать олицетворение каждый раз, когда мне нужно записать в реестр. (

3 голосов
/ 24 февраля 2011

Я не понимаю, как Microsoft может утверждать, что проверки безопасности не будут выполняться. Я быстро взглянул на код, и эти методы - не что иное, как обертки вокруг собственных функций RegOpenKeyEx / RegCreateKeyEx, поэтому, очевидно, будут проводиться проверки безопасности. RegistryKeyPermissionCheck, кажется, контролирует, выполняет ли сам класс RegistryKey собственную внутреннюю проверку безопасности перед P / Invoking собственных функций. Таким образом, вы можете передать любые флаги, которые вы хотите, этому классу RegistryKey, но если базовый раздел реестра не предоставляет вам доступ, вы получите исключение, запрещающее доступ.

Что касается LogonUser (), из того, что я прочитал, кажется, что он всегда даст вам токен с ограничениями.

...