Детект прав админа у пользователя
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.03.24 16:20
Оценка:
В продукте есть функционал по обновлению стороннего софта. Для большинства обновлений требуются права администратора. В этом случае пользователю выдается стандартный виндовый диалог ввода учетки и пароля.

Стоит задача понять, что пользователь имеет права локального админа (и стало быть способен произвести обновление софта).

Желательно сделать проверку без привлечения лишних (особенно сторонних) либ.

Как проще всего это сделать?

На входе есть имя учетки (включая домен или имя компа) и пароль.

Нашел вот такой пусть:
        bool valid = false;
        using (var context = new PrincipalContext(ContextType.Machine))
        {
            valid = context.ValidateCredentials(userName, password);

            var grp = GroupPrincipal.FindByIdentity(context, IdentityType.SamAccountName, "Administators");
            var user = UserPrincipal.FindByIdentity(context, userName);

            foreach (Principal p in user.GetAuthorizationGroups())
            {
                Debug.WriteLine(p);
                if (p.ToString() == "Administrators")
                {
                    // Administrator
                }
            }
        }


Но как-то перебором искать так себе идея. Плюс приходится использовать нюгет-пакет System.DirectoryServices.AccountManagement, что добавляет лишнего веса и хлопот.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Отредактировано 28.03.2024 16:36 VladD2 . Предыдущая версия .
Re: Детект прав админа у пользователя
От: Pavel Dvorkin Россия  
Дата: 28.03.24 16:43
Оценка: 138 (5)
Здравствуйте, VladD2, Вы писали:

VD>В продукте есть функционал по обновлению стороннего софта. Для большинства обновлений требуются права администратора. В этом случае пользователю выдается стандартный виндовый диалог ввода учетки и пароля.


VD>Стоит задача понять, что пользователь имеет права локального админа (и стало быть способен произвести обновление софта).


VD>Желательно сделать проверку без привлечения лишних (особенно сторонних) либ.


VD>Как проще всего это сделать?


VD>На входе есть имя учетки (включая домен или имя компа) и пароль.


LogonUser взять здесь и получить safeTokenHandle

https://learn.microsoft.com/ru-ru/dotnet/api/system.security.principal.windowsimpersonationcontext?view=netframework-4.8.1&viewFallbackFrom=net-8.0

Потом сделать WindowsIdentity по нему

https://learn.microsoft.com/ru-ru/dotnet/api/system.security.principal.windowsidentity.-ctor?view=net-8.0#system-security-principal-windowsidentity-ctor(system-intptr)

А потом по нему получить WindowsPrincipal и проверить роль

https://stackoverflow.com/questions/3600322/check-if-the-current-user-is-administrator

После всех действий в WinAPI надо закрыть handle — CloseHandle на safeTokenHandle.

Как-то примерно так
With best regards
Pavel Dvorkin
Отредактировано 28.03.2024 16:46 Pavel Dvorkin . Предыдущая версия .
Re: Детект прав админа у пользователя
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.03.24 17:24
Оценка:
Здравствуйте, VladD2, Вы писали:

В итоге получилось вот такая функция:
    public static bool IsAdministratorNoCache(string userName)
    {
        var parts = userName.Split('\\');
        var contextType = parts.Length == 2 && parts[0].Equals(Environment.MachineName, StringComparison.OrdinalIgnoreCase) ? ContextType.Machine : ContextType.Domain;
        PrincipalContext context;
        try
        {
            Domain.GetComputerDomain();
            try
            {
                context = new PrincipalContext(contextType);
            }
            catch (PrincipalServerDownException)
            {
                // can't access domain, check local machine instead 
                return false;
            }
        }
        catch (ActiveDirectoryObjectNotFoundException)
        {
            // not in a domain
            context = new PrincipalContext(ContextType.Machine);
        }

        var up = UserPrincipal.FindByIdentity(context, userName);

        if (up == null)
            return false;

        PrincipalSearchResult<Principal> authGroups = up.GetAuthorizationGroups();
        return authGroups.Any(principal => principal.Sid.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid)
                                        || principal.Sid.IsWellKnown(WellKnownSidType.AccountAdministratorSid));
    }


Можно придумать что-то по умнее?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Детект прав админа у пользователя
От: m2user  
Дата: 28.03.24 17:44
Оценка: 53 (1) +1
PD>https://learn.microsoft.com/ru-ru/dotnet/api/system.security.principal.windowsimpersonationcontext?view=netframework-4.8.1&amp;viewFallbackFrom=net-8.0

Там в примере LOGON32_LOGON_INTERACTIVE, что при включенном UAC даст отрицательный результат.
Т.е. важно при проверке использовать тот logon type, который будет фактически использован при выполнении под этой учеткой рабочих операций.
Re[3]: Детект прав админа у пользователя
От: Pavel Dvorkin Россия  
Дата: 28.03.24 18:15
Оценка:
Здравствуйте, m2user, Вы писали:

PD>>https://learn.microsoft.com/ru-ru/dotnet/api/system.security.principal.windowsimpersonationcontext?view=netframework-4.8.1&amp;viewFallbackFrom=net-8.0


M>Там в примере LOGON32_LOGON_INTERACTIVE, что при включенном UAC даст отрицательный результат.

M>Т.е. важно при проверке использовать тот logon type, который будет фактически использован при выполнении под этой учеткой рабочих операций.

Да, верно
Вот тут советуют LOGON32_LOGON_BATCH

https://stackoverflow.com/questions/39403050/how-to-call-logonuser-to-get-a-non-restricted-full-token-inside-a-windows-serv
With best regards
Pavel Dvorkin
Re: Детект прав админа у пользователя
От: bnk СССР http://unmanagedvisio.com/
Дата: 29.03.24 13:53
Оценка: 14 (2) +2
Здравствуйте, VladD2, Вы писали:

Как вариант, добавить в манифест программы-обновлялки (EXE) что она должна запускаться только админом.
Тогда и диалог с выбором пользователя не придется показывать, и запустить ее сможет только админ.

Add New Item => Application Manifest file => раскомментировать строчку "admin only"

Промпт на дабл-клик система будет показывать сама.
Также если запускать из основной программы этот "обновлятор" через startInfo.Verb = "runas", то диалог покажется системой автоматически если юзер не админ, т.е.

Process.Start(new ProcessStartInfo(@"MyUpdate.exe") { Verb = "runas" });

В общем меньше телодвижений чем делать вручную, и идеологически как-то правильнее IMHO.

https://stackoverflow.com/questions/2818179/how-do-i-force-my-net-application-to-run-as-administrator
Отредактировано 29.03.2024 14:21 bnk . Предыдущая версия . Еще …
Отредактировано 29.03.2024 13:56 bnk . Предыдущая версия .
Re[2]: Детект прав админа у пользователя
От: VladD2 Российская Империя www.nemerle.org
Дата: 29.03.24 15:25
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>А потом по нему получить WindowsPrincipal


До сюда все пучком...

PD>и проверить роль


PD>https://stackoverflow.com/questions/3600322/check-if-the-current-user-is-administrator


А вот это не работает.

    public static bool IsLoginLocalAdministrator(string userName, string domainName, string password)
    {
        SafeTokenHandle safeTokenHandle;
        try
        {
            const int LOGON32_PROVIDER_DEFAULT = 0;
            //This parameter causes LogonUser to create a primary token.
            const int LOGON32_LOGON_INTERACTIVE = 2;

            // Call LogonUser to obtain a handle to an access token.
            bool returnValue = LogonUser(userName, domainName, password,
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                out safeTokenHandle);

            if (!returnValue)
            {
                var ret = Marshal.GetLastWin32Error();
                throw new System.ComponentModel.Win32Exception(ret);
            }

            using (safeTokenHandle)
            {
                using WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle());
                return WindowsIdentity.RunImpersonated(newId.AccessToken, () =>
                    {
                        // Вот тут возвращает false на учетку локального админа :(
                        return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);
                    });
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine("Exception occurred. " + ex.Message);
        }

        return false;
    }


При этом в списке права локального админа есть:


ЧЯДНТ?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Отредактировано 29.03.2024 15:49 VladD2 . Предыдущая версия . Еще …
Отредактировано 29.03.2024 15:35 VladD2 . Предыдущая версия .
Re[3]: Детект прав админа у пользователя
От: Pavel Dvorkin Россия  
Дата: 29.03.24 15:54
Оценка: 53 (1)
Здравствуйте, VladD2, Вы писали:

Во-первых, все же, видимо, надо LOGON32_LOGON_BATCH, так как LOGON32_LOGON_INTERACTIVE вернет при включенном UAC токен юзера, а не админа.


А дальше надо было не брать весь код, а применить к ситуации

VD> using (safeTokenHandle)

VD> {
VD> using WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle());

Не надо тут никакой имперсонализации,

VD> return WindowsIdentity.RunImpersonated(newId.AccessToken, () =>

VD> {
VD> // Вот тут возвращает false на учетку локального админа

И GetCurrent тут не нужен. Это для случая текущего юзера

VD> return new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator);


Есть WindowsIdentity newId. Вот по нему и надо сделать new WindowsPrincipal и у него IsInRole

WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle());
WindowsPrincipal principal = new WindowsPrincipal(newId);
return principal.IsInRole(WindowsBuiltInRole.Administrator);

using сам расставь.

и CloseHandle не забудь.
With best regards
Pavel Dvorkin
Re[4]: Детект прав админа у пользователя
От: VladD2 Российская Империя www.nemerle.org
Дата: 29.03.24 16:43
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Есть WindowsIdentity newId. Вот по нему и надо сделать new WindowsPrincipal и у него IsInRole


PD>WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle());

PD>WindowsPrincipal principal = new WindowsPrincipal(newId);
PD>return principal.IsInRole(WindowsBuiltInRole.Administrator);

Так заработало. Но только с локальной учёткой. С доменной не работает.

С если заменить LOGON32_LOGON_BATCH обратно на LOGON32_LOGON_INTERACTIVE, вроде работает. Но при этом\ bool isAdmin =
windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);

начинает возвращать false.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Отредактировано 29.03.2024 16:45 VladD2 . Предыдущая версия .
Re[5]: Детект прав админа у пользователя
От: VladD2 Российская Империя www.nemerle.org
Дата: 29.03.24 16:50
Оценка: 21 (2)
Здравствуйте, VladD2, Вы писали:

VD>Так заработало. Но только с локальной учёткой. С доменной не работает.


VD>С если заменить LOGON32_LOGON_BATCH обратно на LOGON32_LOGON_INTERACTIVE, вроде работает. Но при этом\ bool isAdmin =

VD>
VD>windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
VD>

VD>начинает возвращать false.

Всё заработало с LOGON32_LOGON_NETWORK!

Всем спасибо!
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Детект прав админа у пользователя
От: VladD2 Российская Империя www.nemerle.org
Дата: 29.03.24 17:06
Оценка: 20 (1)
Здравствуйте, VladD2, Вы писали:

VD>В продукте есть функционал по обновлению стороннего софта. Для большинства обновлений требуются права администратора. В этом случае пользователю выдается стандартный виндовый диалог ввода учетки и пароля.


Если кому интересно, вот финальный, рабочий код:
using System.Runtime.InteropServices;
using System.Security.Principal;
using Microsoft.Win32.SafeHandles;
using System.Security;
using System.Net;
using System.Runtime.Versioning;
using System.Text;
using System.Windows;
using System.Diagnostics;
using System.Windows.Interop;

namespace WpfCred;

internal class Credentials
{
    const int CREDUI_MAX_USERNAME_LENGTH = 256 + 1 + 256;
    const int CREDUI_MAX_DOMAIN_TARGET_LENGTH = 256 + 1 + 80;
    const int CREDUI_MAX_PASSWORD_LENGTH = 512 / 2;

    [Flags]
    private enum CredUiWinSecure : uint
    {
        /// <summary>
        /// The caller is requesting that the credential provider return the user name and password in plain text.
        /// This value cannot be combined with SECURE_PROMPT.
        /// </summary>
        Generic = 0x1,
        /// <summary> The Save check box is displayed in the dialog box.</summary>
        Checkbox = 0x2,
        /// <summary>
        /// Only credential providers that support the authentication package specified by the pulAuthPackage parameter should be enumerated.
        /// This value cannot be combined with IN_CRED_ONLY.
        /// </summary>
        AuthpackageOnly = 0x10,
        /// <summary>
        /// Only the credentials specified by the pvInAuthBuffer parameter for the authentication package specified by the pulAuthPackage parameter should be enumerated.
        /// If this flag is set, and the pvInAuthBuffer parameter is NULL, the function fails.
        /// 
        /// This value cannot be combined with AuthpackageOnly.
        /// </summary>
        InCredOnly = 0x20,
        /// <summary>
        /// Credential providers should enumerate only administrators. This value is intended for User Account Control (UAC) purposes only.We recommend that external callers not set this flag.
        /// </summary>
        EnumerateAdmins = 0x100,
        /// <summary>
        /// Only the incoming credentials for the authentication package specified by the pulAuthPackage parameter should be enumerated.
        /// </summary>
        EnumerateCurrentUser = 0x200,
        /// <summary>
        /// The credential dialog box should be displayed on the secure desktop.This value cannot be combined with Generic
        /// Windows Vista:  This value is supported beginning with Windows Vista with SP1.
        /// </summary>
        SecurePrompt = 0x1000,
        /// <summary>
        /// The credential dialog box is invoked by the SspiPromptForCredentials function, and the client is prompted before a prior handshake.If SSPIPFC_NO_CHECKBOX is passed in the pvInAuthBuffer parameter, then the credential provider should not display the check box.
        /// Windows Vista:  This value is supported beginning with Windows Vista with SP1.
        /// </summary>
        PrePrompting = 0x2000,
        /// <summary>
        /// The credential provider will not pack the AAD authority name. This is only applied to Azure AD joined devices.
        /// Windows 10, version 1607:  This value is supported beginning with Windows 10, version 1607.
        /// </summary>
        NotPackAadAuthority = 0x40000,
        /// <summary>
        /// The credential provider should align the credential BLOB pointed to by the ppvOutAuthBuffer parameter to a 32-bit boundary, even if the provider is running on a 64-bit system.
        /// </summary>
        Pack32Wow = 0x10000000,
        /// <summary>
        /// Windows Hello credentials will be packed in a smart card auth buffer.This only applies to the face, fingerprint, and PIN credential providers.
        /// Windows 10, version 1809:  This value is supported beginning with Windows 10, version 1809.
        /// </summary>
        WindowsHello = 0x80000000
    }

    private enum CRED_PACK
    {
        None = 0,
        PROTECTED_CREDENTIALS   = 0x1,
        WOW_BUFFER              = 0x2,
        GENERIC_CREDENTIALS     = 0x4,
        ID_PROVIDER_CREDENTIALS = 0x8,
    }


    [DllImport("kernel32.dll", CharSet = CharSet.Auto, EntryPoint="RtlSecureZeroMemory")]
    private static extern void SecureZeroMemory(IntPtr ptr, IntPtr cnt);

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    private struct CREDUI_INFO
    {
        public int cbSize;
        public IntPtr hwndParent;
        public string pszMessageText;
        public string pszCaptionText;
        public IntPtr hbmBanner;
    }

    [DllImport("credui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool CredUnPackAuthenticationBuffer(CRED_PACK dwFlags,
                                                               IntPtr pAuthBuffer,
                                                               uint cbAuthBuffer,
                                                               StringBuilder pszUserName,
                                                               ref int pcchMaxUserName,
                                                               StringBuilder pszDomainName,
                                                               ref int pcchMaxDomainame,
                                                               StringBuilder pszPassword,
                                                               ref int pcchMaxPassword);
    // Decode credentials
    [DllImport("credui.dll", CharSet = CharSet.Auto)]
    private static extern int CredUIPromptForWindowsCredentials(ref CREDUI_INFO notUsedHere,
                                                                 int authError,
                                                                 ref uint authPackage,
                                                                 IntPtr InAuthBuffer,
                                                                 uint InAuthBufferSize,
                                                                 out IntPtr refOutAuthBuffer,
                                                                 out uint refOutAuthBufferSize,
                                                                 ref bool fSave,
                                                                 CredUiWinSecure flags);


    [DllImport("ole32.dll")]
    [SuppressUnmanagedCodeSecurity]
    [ResourceExposure(ResourceScope.None)]
    private static extern void CoTaskMemFree(IntPtr ptr);

    private enum Logon32
    {
        Interactive = 2,
        Network = 3,
        Batch = 4,
    }

    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    private static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
        Logon32 dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);

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

    private sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        private SafeTokenHandle() : base(true) { }

        [DllImport("kernel32.dll")]
        [SuppressUnmanagedCodeSecurity]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr handle);

        protected override bool ReleaseHandle() => CloseHandle(handle);
    }

    private enum IsLoginResult
    {
        IsAdmin,
        PasswordIncorrect,
        IsNotAdmin,
    }

    private static IsLoginResult IsLoginLocalAdministrator(string userName, string domainName, string password)
    {
        var parts = userName.Split('\\');

        if (parts.Length == 2)
        {
            userName = parts[1];
            if (string.IsNullOrEmpty(domainName))
                domainName = parts[0];
        }

        try
        {
            const int LOGON32_PROVIDER_DEFAULT = 0;

            // Call LogonUser to obtain a handle to an access token.
            bool returnValue = LogonUser(userName, domainName, password,
                Logon32.Network, LOGON32_PROVIDER_DEFAULT,
                out SafeTokenHandle safeTokenHandle);

            if (!returnValue)
            {
                var passwordIncorrect = 1326;
                var ret = Marshal.GetLastWin32Error();
                if (ret == passwordIncorrect)
                    return IsLoginResult.PasswordIncorrect;

                throw new System.ComponentModel.Win32Exception(ret);
            }

            using (safeTokenHandle)
            {
                // Use the token handle returned by LogonUser.
                using WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle());

                var windowsPrincipal = new WindowsPrincipal(newId);
                bool isAdmin = windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
                return isAdmin ? IsLoginResult.IsAdmin : IsLoginResult.IsNotAdmin;
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine("Exception occurred. " + ex.Message);
        }

        return IsLoginResult.IsNotAdmin;
    }

    public static NetworkCredential? GetCredentials(Window parent, string caption, string message, NetworkCredential? defaultCreds = null)
    {
        var inCredBuffer = IntPtr.Zero;
        var inCredSize = 0u;
        var hWndParent = new WindowInteropHelper(parent).EnsureHandle();
        var credui = new CREDUI_INFO
        {
            hwndParent = hWndParent,
            pszCaptionText = caption,
            pszMessageText = message,
        };
        credui.cbSize = Marshal.SizeOf(credui);

        for (;;)
        {
            uint authPackage = 0;
            var save = false;
            var usernameBuf = new StringBuilder(CREDUI_MAX_USERNAME_LENGTH + 1);
            var passwordBuf = new StringBuilder(CREDUI_MAX_PASSWORD_LENGTH + 1);
            var domainBuf = new StringBuilder(CREDUI_MAX_DOMAIN_TARGET_LENGTH);
            var userNameLen = CREDUI_MAX_USERNAME_LENGTH;
            var domainLen = CREDUI_MAX_DOMAIN_TARGET_LENGTH;
            var passwordLen = CREDUI_MAX_PASSWORD_LENGTH;

            var result = CredUIPromptForWindowsCredentials(
                ref credui,
                0,
                ref authPackage,
                InAuthBuffer: inCredBuffer,
                InAuthBufferSize: inCredSize,
                out var outCredBuffer,
                out var outCredSize,
                ref save,
                CredUiWinSecure.EnumerateAdmins /*| CredUiWinSecure.Checkbox*/ | CredUiWinSecure.WindowsHello
            );

            inCredBuffer = outCredBuffer;
            inCredSize = outCredSize;

            if (result != 0)
            {
                var res = Marshal.GetLastWin32Error();
                Debug.WriteLine(new System.ComponentModel.Win32Exception(res).Message);
                return null;
            }

            var packAuthRes = CredUnPackAuthenticationBuffer(CRED_PACK.PROTECTED_CREDENTIALS, outCredBuffer, outCredSize,
                usernameBuf, ref userNameLen,
                domainBuf, ref domainLen,
                passwordBuf, ref passwordLen);

            if (!packAuthRes)
            {
                var res = Marshal.GetLastWin32Error();
                Debug.WriteLine(new System.ComponentModel.Win32Exception(res).Message);
                if (outCredBuffer != IntPtr.Zero)
                    CoTaskMemFree(outCredBuffer);
                return null;
            }

            var userName = usernameBuf.ToString();
            var domain = domainBuf.ToString();
            var password = passwordBuf.ToString();
            var isLoginResult = IsLoginLocalAdministrator(userName, domain, password);

            if (outCredBuffer != IntPtr.Zero)
                CoTaskMemFree(outCredBuffer);

            switch (isLoginResult)
            {
                case IsLoginResult.IsAdmin:
                    return new NetworkCredential
                    {
                        UserName = userName,
                        Domain = domain,
                        Password = password,
                    };
                case IsLoginResult.PasswordIncorrect:
                    MessageBox.Show(parent, "Неверный пароль.", caption, MessageBoxButton.OK, MessageBoxImage.Error);
                    continue;
                case IsLoginResult.IsNotAdmin:
                default:
                    MessageBox.Show(parent, "Пользователь не является локальным администратором.", caption, MessageBoxButton.OK, MessageBoxImage.Error);
                    continue;
            }
        }
    }
} // end Class
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Отредактировано 29.03.2024 17:10 VladD2 . Предыдущая версия .
Re[2]: Детект прав админа у пользователя
От: m2user  
Дата: 29.03.24 17:28
Оценка: 55 (1)
VD>Если кому интересно, вот финальный, рабочий код:

Импорт CoTaskMemFree из "ole32.dll" лишний. Есть же Marshal.FreeCoTaskMem(IntPtr).
Re[5]: Детект прав админа у пользователя
От: Pavel Dvorkin Россия  
Дата: 30.03.24 02:53
Оценка:
Здравствуйте, VladD2, Вы писали:


VD>Так заработало. Но только с локальной учёткой. С доменной не работает.


VD>С если заменить LOGON32_LOGON_BATCH обратно на LOGON32_LOGON_INTERACTIVE, вроде работает. Но при этом\ bool isAdmin =

VD>
VD>windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
VD>

VD>начинает возвращать false.

Тут вот какая ситуация. Начиная с Vista, при включенном UAC при логине админа создаются 2 токена — один админский, другой юзеровский. Вроде как LOGON32_LOGON_INTERACTIVE возвращает юзеровский, а LOGON32_LOGON_BATCH — админский. В конце концов можно попробовать оба, тебе же не работать с ними потом надо, а только выяснить.

Ну а что касается ролей, то можно попробовать IsInRole по RID (это младшая часть SID)

DOMAINNAME\Administrator 0x1F4
DOMAINNAME\Domain Admins 0x200
BUILTIN\Administrators 0x220

https://learn.microsoft.com/ru-ru/dotnet/api/system.security.principal.windowsprincipal.isinrole?view=net-7.0#system-security-principal-windowsprincipal-isinrole(system-int32)

Если хоть что-то есть хоть в одном токене — ответ да.
With best regards
Pavel Dvorkin
Re[2]: Детект прав админа у пользователя
От: VladD2 Российская Империя www.nemerle.org
Дата: 01.04.24 15:31
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>Как вариант, добавить в манифест программы-обновлялки (EXE) что она должна запускаться только админом.

bnk>Тогда и диалог с выбором пользователя не придется показывать, и запустить ее сможет только админ.

Речь идет об обновлении стороннего софта. Нужно запустить кучу уже скачанных инсталляторов, чтобы пользователь единожды введя пароль получил возможность обновить весь софт. Сейчас зачем-то просто убирается возможность накатить обновления:



А нужно чтобы можно было ввести админский пароль и нажатием выделенную кнопку "Обновить" установить все обновления без выдачи отдельных запросов на ввод админского логина:

Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Детект прав админа у пользователя
От: bnk СССР http://unmanagedvisio.com/
Дата: 01.04.24 15:40
Оценка: +2
Здравствуйте, VladD2, Вы писали:

VD>А нужно чтобы можно было ввести админский пароль и нажатием выделенную кнопку "Обновить" установить все обновления без выдачи отдельных запросов на ввод админского логина:


Я примерно понял. Идея была такая — делаешь отельный EXE, который требует админских прав (см. пункт про манифест), и вот этот EXE все обновляет.
"Основная" программа где UI запускает этот EXE, через "runas", тогда пользователю покажется диалог на ввод админского пароля автоматически, если он не админ.
Параметры этому EXE как-то передать (что обновлять), ну через файл там временный или через командную строку или еще как.

В общем по тому же принципу, что работает Windows Installer.
Или другие программы (типа того же FAR например) для выполнения "привилегированных" операций.

Это если тебе важно чтобы "основная" программа с UI не требовала админских прав.
А так можно просто на нее потребовать (добавить тот самый манифест), и все дочерние процессы автоматически будут под админом запускаться.
Отредактировано 01.04.2024 15:41 bnk . Предыдущая версия .
Re[6]: Детект прав админа у пользователя
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.06.24 17:02
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Тут вот какая ситуация. Начиная с Vista, при включенном UAC при логине админа создаются 2 токена — один админский, другой юзеровский. Вроде как LOGON32_LOGON_INTERACTIVE возвращает юзеровский, а LOGON32_LOGON_BATCH — админский. В конце концов можно попробовать оба, тебе же не работать с ними потом надо, а только выяснить.


Тут еще такой вопрос возник. Получить нужный токен с помощью LogonUser я смог.

Но есть еще задача проверить, что текущий юзер является админом. И тут тоже вылезает проблема с тое о чем ты написал выше. Вот такой код:
    using var windowsIdentity = WindowsIdentity.GetCurrent();
    var windowsPrincipal = new WindowsPrincipal(windowsIdentity);
    var isAdmin = windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
    Console.WriteLine($"You are is {(isAdmin ? "" : "not ")}administrator.");

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

Как узнать, что текущий пользователь админ (т.е. что текущий токен можно элевировать до админа)?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: Детект прав админа у пользователя
От: m2user  
Дата: 05.06.24 17:22
Оценка:
VD>Как узнать, что текущий пользователь админ (т.е. что текущий токен можно элевировать до админа)?

проверить WindowsPrincipal.UserClaims. См. свою картинку в сообщении https://rsdn.org/forum/dotnet/8721245?tree=tree
Автор: VladD2
Дата: 29.03.24
.
Re[8]: Детект прав админа у пользователя
От: VladD2 Российская Империя www.nemerle.org
Дата: 06.06.24 12:20
Оценка:
Здравствуйте, m2user, Вы писали:

M>проверить WindowsPrincipal.UserClaims. См. свою картинку в сообщении https://rsdn.org/forum/dotnet/8721245?tree=tree
Автор: VladD2
Дата: 29.03.24
.


Но неужели для этого нет готовой функции?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Детект прав админа у пользователя
От: m2user  
Дата: 06.06.24 13:56
Оценка:
VD>Но неужели для этого нет готовой функции?

Хм, можно у токена проверить TokenElevationType через GetTokenInformation, и если он не elevated, то вытащить linkedToken (через GetTokenInformation с параметром TokenLinkedToken).
И вот на этом токене уже проверять IsInRole. В теории должно сработать без доп. привилегий (SeTcbPrivilege, см. https://stackoverflow.com/a/39403260/23213344)

Но мне кажется, UserClaims проверить проще будет.
Re[7]: Детект прав админа у пользователя
От: Pavel Dvorkin Россия  
Дата: 06.06.24 15:00
Оценка:
Здравствуйте, VladD2, Вы писали
VD>Но есть еще задача проверить, что текущий юзер является админом. И тут тоже вылезает проблема с тое о чем ты написал выше. Вот такой код:
VD>
VD>    using var windowsIdentity = WindowsIdentity.GetCurrent();
VD>    var windowsPrincipal = new WindowsPrincipal(windowsIdentity);
VD>    var isAdmin = windowsPrincipal.IsInRole(WindowsBuiltInRole.Administrator);
VD>    Console.WriteLine($"You are is {(isAdmin ? "" : "not ")}administrator.");
VD>

VD>Отлично работе из консоли открытой в режиме администратора, но при обычном запуске не позволяет понять является ли юезер админом.

VD>Как узнать, что текущий пользователь админ (т.е. что текущий токен можно элевировать до админа)?


Если юзер входит в группу администраторов, то для него создается 2 токена. Один админовский , другой юзеровский. Первый используется при запуске программ с правами админа, второй — без этих прав. Никакой элевации токенов не существует как понятия.

WindowsIdentity.GetCurrent() возвращает текущий токен.

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

Либо посмотри тут

https://stackoverflow.com/questions/3600322/check-if-the-current-user-is-administrator

IsCurrentUserAdmin

или

IsCurrentUserInAdminGroup
With best regards
Pavel Dvorkin
Отредактировано 06.06.2024 15:06 Pavel Dvorkin . Предыдущая версия .
Re[10]: Детект прав админа у пользователя
От: VladD2 Российская Империя www.nemerle.org
Дата: 06.06.24 16:11
Оценка:
Здравствуйте, m2user, Вы писали:

M>Но мне кажется, UserClaims проверить проще будет.


Да, согласен. Просто как-то не надежно выглядит.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Детект прав админа у пользователя
От: VladD2 Российская Империя www.nemerle.org
Дата: 06.06.24 16:13
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Поэтому если нужно узнать, является ли юзер админом из программы, запущенной без прав админа, то можно, конечно, сделать в ней logonUser с админовским входом как в прошлый раз


LogonUser подразумевает ввод данных логина. Это не то.

PD>Либо посмотри тут

PD>https://stackoverflow.com/questions/3600322/check-if-the-current-user-is-administrator
PD>IsCurrentUserAdmin
PD>или
PD>IsCurrentUserInAdminGroup

Это то, что предложил m2user здесь
Автор: m2user
Дата: 05.06 20:22
. Похоже действительно самый простой способ. У нас был рабочий вариант, но он в первый раз втыкал на несколько секунд.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: Детект прав админа у пользователя
От: Pavel Dvorkin Россия  
Дата: 07.06.24 02:36
Оценка:
Здравствуйте, VladD2, Вы писали:


PD>>IsCurrentUserInAdminGroup


VD>Это то, что предложил m2user здесь
Автор: m2user
Дата: 05.06 20:22
. Похоже действительно самый простой способ. У нас был рабочий вариант, но он в первый раз втыкал на несколько секунд.


Видимо, да. Единственное, что тут можно сделать — проверить, принадлежит ли юзер к группе админов.
Если нет домена, то все должно работать.
Если же он есть, то может быть сложнее. Он может быть членом Domain Admins, а она в свою очередь входит в группу локальных админов после загрузки компьютера.
Поэтому по ссылке выше в одном из примеров делаются все проверки

return authGroups.Any(principal =>
principal.Sid.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid) ||
principal.Sid.IsWellKnown(WellKnownSidType.AccountDomainAdminsSid) ||
principal.Sid.IsWellKnown(WellKnownSidType.AccountAdministratorSid) ||
principal.Sid.IsWellKnown(WellKnownSidType.AccountEnterpriseAdminsSid));
With best regards
Pavel Dvorkin
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.