C#: запуск процесса от имени настоящего администратора с жестко заданными привилегиями

Мне нужно установить различные настройки без вывода сообщений с правами администратора. Мне приходится жестко кодировать привилегии, потому что пользователи не знают имени пользователя и пароля для самостоятельной установки настроек.

Я пробовал два разных подхода.

  1. ProcessStartInfo с именем пользователя, паролем и UseShellExecute = false.
  2. Олицетворение пользователя с помощью

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LogonUser(...);
    

В обоих сценариях windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator) возвращает false, и мои установки не запускаются из-за недостаточных прав.

Странное поведение: LogonUser всегда возвращает true, даже с неверными учетными данными.

Вот класс олицетворения:

namespace BlackBlade.Utilities
{
    /// <summary>
    /// Quelle: http://www.blackbladeinc.com/en-us/community/blogs/archive/2009/08/10/runas-in-c.aspx
    /// </summary>
    public class SecurityUtilities
    {
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken);

        public delegate void RunAsDelegate();

        public static void RunAs(RunAsDelegate methodToRunAs, string username, string password)
        {
            string userName;

            string domain;
            if (username.IndexOf('\\') > 0)
            {
                //a domain name was supplied
                string[] usernameArray = username.Split('\\');
                userName = usernameArray[1];
                domain = usernameArray[0];
            }
            else
            {
                //there was no domain name supplied
                userName = username;
                domain = ".";
            }
            RunAs(methodToRunAs, userName, password, domain);
        }

        public static void RunAs(RunAsDelegate methodToRunAs, string username, string password, string domain)
        {
            IntPtr userToken;
            WindowsIdentity adminIdentity = null;
            WindowsImpersonationContext adminImpersonationContext = null;

            try
            {
                if (LogonUser(username, string.IsNullOrEmpty(domain) ? "." : domain, password, 9, 0, out userToken))
                {
                    //the impersonation suceeded
                    adminIdentity = new WindowsIdentity(userToken);
                    adminImpersonationContext = adminIdentity.Impersonate();

                    // todo: Entfernen.
                    WindowsPrincipal p = new WindowsPrincipal(adminIdentity);
                    MessageBox.Show(p.IsInRole(WindowsBuiltInRole.Administrator).ToString());

                    //run the delegate method
                    //methodToRunAs();
                }
                else
                    throw new Exception(string.Format("Could not impersonate user {0} in domain {1} with the specified password.", username, domain));
            }
            catch (Exception se)
            {
                int ret = Marshal.GetLastWin32Error();
                if (adminImpersonationContext != null)
                    adminImpersonationContext.Undo();
                throw new Exception("Error code: " + ret.ToString(), se);
            }
            finally
            {
                //revert to self
                if (adminImpersonationContext != null)
                    adminImpersonationContext.Undo();
            }
        }
    }
}

person timmkrause    schedule 28.06.2012    source источник
comment
Пожалуйста, дайте вашему вопросу более осмысленное название!   -  person abatishchev    schedule 28.06.2012
comment
Спасибо за то, что делаете SO лучше :)   -  person abatishchev    schedule 28.06.2012
comment
LogonUser правильно возвращает true даже для неверных учетных данных, потому что с типом входа NEW_CREDENTIALS он фактически не входит в систему с указанным пользователем! Он просто сохраняет учетные данные в копии токена вызывающего пользователя для использования в сетевых операциях (подумайте net use /U). Если вы хотите зарегистрировать пользователя на локальном компьютере, используйте ИНТЕРАКТИВНЫЙ тип входа, предложенный rdkleine.   -  person Anton Tykhyy    schedule 28.06.2012
comment
Основываясь на ваших комментариях, я должен понизить этот вопрос. Вы не объясняете подробно ситуацию с пользователями.   -  person Security Hound    schedule 28.06.2012
comment
Но я описал то, что ХОЧУ но вроде никому нет дела и описаны другие способы. Я знаю о проблемах безопасности, но это фактическая установка.   -  person timmkrause    schedule 28.06.2012


Ответы (3)


Добавить манифест к процессу, который вы запускаете с помощью RunAs, чтобы запросить повышение прав.

Редактировать. Сначала запустите процесс, используя свои известные учетные данные администратора, либо с помощью LogonUser/CreateProcessAsUser, либо с помощью CreateProcessWithLogon. Затем проверьте наличие реальных прав администратора (возможно, UAC отключен) и, если необходимо, запустите этот процесс (работающий от имени администратора без прав администратора) с помощью ShellExecuteEx, используя глагол runas. Это единственный способ. UAC был специально разработан для запрета повышения прав без подтверждения пользователя.

Пользователи должны будут подтвердить повышение прав, если только UAC не отключен. Для лучшего взаимодействия с пользователем (менее страшное окно сообщения) получите сертификат подписи кода и подпишите этот исполняемый файл.

person Anton Tykhyy    schedule 28.06.2012
comment
Нет варианта. Пользователи не имеют прав администратора и доступа к какой-либо учетной записи администратора. Я должен поднять его программно. - person timmkrause; 28.06.2012
comment
Вы не можете повысить уровень программно без подтверждения пользователя (подсказка UAC). Вот для чего нужен UAC. Любой обходной путь является дырой в безопасности и будет исправлен Microsoft как можно скорее. - person Anton Tykhyy; 28.06.2012
comment
Итак, вы говорите, что с помощью техники, которую вы упомянули, можно добиться полного административного повышения? - person timmkrause; 06.07.2012
comment
запустить еще одну свою копию с помощью ShellExecuteEx, используя глагол runas. Но это приведет к появлению всплывающего окна UAC, которое запрашивает повышение прав, верно? Таким образом, пользователь должен снова ввести имя пользователя и пароль, если у него нет прав администратора, и просто нажать «Да», если они у него есть. Это правильно? Таким образом, это то же самое поведение, что и применение файла манифеста. - person timmkrause; 06.07.2012
comment
Вам придется попробовать. Поскольку процесс, вызывающий ShellExecuteEx, будет выполняться от имени администратора (без повышенных прав), возможно, пользователю не потребуется вводить учетные данные. - person Anton Tykhyy; 06.07.2012
comment
Ах хорошо. Так что по-настоящему работать в повышенном режиме невозможно. Хорошо. - person timmkrause; 06.07.2012

Используйте групповую политику для развертывания установщика MSI или EXE.

В качестве альтернативы используйте планировщик заданий, чтобы запустить программу установки от имени учетной записи локальной системы.

person Ben    schedule 28.06.2012
comment
Невозможно. Устройства бездоменные. Второй совет тоже невозможен. Мы отправим USB-накопитель нашим пользователям, и это должно быть так же просто, как щелкнуть наш setup.exe. - person timmkrause; 28.06.2012
comment
Итак, у кого есть вход в систему с правами администратора? Пользователь? Ты? Сисадмин пользователя? Правильный ответ — пометить вашу настройку как требующую прав администратора, а затем позволить системному администратору пользователя позаботиться об этом. - person Ben; 28.06.2012
comment
Другими словами: Не делайте этого. Вы в основном просите своих пользователей отключить безопасность. Любой получатель флэш-накопителей получает пароль администратора для своей машины. Да, безопасность — это боль. - person Ben; 28.06.2012
comment
Я не имею никакого влияния на изменение этого. Ситуация как описано выше. - person timmkrause; 28.06.2012
comment
Пароль не передается им в открытом виде, и код, который его содержит, будет запутан. - person timmkrause; 28.06.2012
comment
код, который его содержит, будет запутан: правильно... вот так всегда работает. Если приз стоит, его достаточно для того, чтобы его взломал один человек, а затем Google позаботится о том, чтобы каждый узнал, как это сделать. Безопасность должна быть сделана правильно. Вы должны кричать «вредоносное ПО» и «мошенничество». - person Remus Rusanu; 28.06.2012
comment
@tkrause - вы также используете шифрование для хранения пароля, который можно взломать, если известен ключ, который должен быть приведен, если вы используете пароль для аутентификации в учетной записи пользователя Windows, что означает, что пароль более или менее находится в простой текст. - person Security Hound; 28.06.2012

Вы пытались установить для dwLogonType значение 2 вместо 9?

http://msdn.microsoft.com/en-us/library/windows/desktop/bb540756(v=vs.85).aspx

Вот пример кода, который работает для меня:

    public const int LOGON32_LOGON_INTERACTIVE = 2;
    public const int LOGON32_PROVIDER_DEFAULT = 0;

    WindowsImpersonationContext impersonationContext;

    [DllImport("advapi32.dll")]
    public static extern int LogonUserA(String lpszUserName,
        String lpszDomain,
        String lpszPassword,
        int dwLogonType,
        int dwLogonProvider,
        ref IntPtr phToken);
    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern int DuplicateToken(IntPtr hToken,
        int impersonationLevel,
        ref IntPtr hNewToken);

    [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    public static extern bool RevertToSelf();

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public static extern bool CloseHandle(IntPtr handle);

        WindowsIdentity tempWindowsIdentity;
        IntPtr token = IntPtr.Zero;
        IntPtr tokenDuplicate = IntPtr.Zero;

        if (RevertToSelf())
        {
            if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
                LOGON32_PROVIDER_DEFAULT, ref token) != 0)
            {
                if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                {
                    tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                    impersonationContext = tempWindowsIdentity.Impersonate();
                    if (impersonationContext != null)
                    {
                        CloseHandle(token);
                        CloseHandle(tokenDuplicate);
                        return true;
                    }
                }
            }
        }
        if (token != IntPtr.Zero)
            CloseHandle(token);
        if (tokenDuplicate != IntPtr.Zero)
            CloseHandle(tokenDuplicate);
person Ralf de Kleine    schedule 28.06.2012
comment
Что произойдет, если вы проверите windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator)? - person timmkrause; 28.06.2012
comment
Будет ложным, если UAC не отключен. - person Anton Tykhyy; 28.06.2012