Вызов IPrincipal.IsInRole в Windows 7 - PullRequest
8 голосов
/ 20 апреля 2010

Мы используем аутентификацию NTLM в нашем приложении, чтобы определить, может ли пользователь выполнять определенные операции. Мы используем IPrincipal их текущего входа в Windows (в приложениях WinForms), вызывая IsInRole , чтобы проверить членство в определенных группах.

Чтобы проверить, что пользователь является локальным администратором на машине, мы используем:

AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
...
bool allowed = Thread.CurrentPrincipal.IsInRole(@"Builtin\Administrators")

Это работает, если текущий пользователь является Administrator пользователем или является другим пользователем, который является членом группы Builtin\Administrators.

В нашем тестировании на Windows 7 мы обнаружили, что это больше не работает, как ожидалось. Пользователь Administrator по-прежнему работает нормально, но любой другой пользователь, являющийся членом группы Builtin\Administrators, возвращает false для вызова IsInRole.

Что может быть причиной этой разницы? У меня есть ощущение, что настройки по умолчанию где-то изменились (возможно, в gpedit), но я не могу найти ничего похожего на виновника.

Ответы [ 5 ]

9 голосов
/ 20 апреля 2010

Проблема в том, что безопасность Windows (также известная как «UAC») мешает вам. Существует специальная обработка ролей администратора, и у вашего пользователя фактически не будет этих ролей, пока он не будет повышен. Роли администратора являются «призрачными» в некотором смысле: присутствующими, но недоступными для проверки разрешений или даже (легко) проверять наличие. Смотрите примечание по адресу: http://msdn.microsoft.com/en-us/library/46ks97y7.aspx

Вот серия, в которой говорится о проблеме, с примером кода, который делает необходимые обходные пути:

Я решил похожую проблему в приложении ASP.NET, создав собственное приглашение UAC и используя имя и пароль для вызова Win32 Logon API. Возможно, вам повезет оказаться в настольном приложении .NET, и в этом случае вы можете использовать обычные запросы на повышение прав.

Вот код C # для проверки прав администратора без повышения прав.

    public const UInt32 TOKEN_DUPLICATE = 0x0002;
    public const UInt32 TOKEN_IMPERSONATE = 0x0004;
    public const UInt32 TOKEN_QUERY = 0x0008;

    public enum TOKEN_ELEVATION_TYPE
    {
        TokenElevationTypeDefault = 1,
        TokenElevationTypeFull,
        TokenElevationTypeLimited
    }

    public enum TOKEN_INFORMATION_CLASS
    {
        TokenUser = 1,
        TokenGroups,
        TokenPrivileges,
        TokenOwner,
        TokenPrimaryGroup,
        TokenDefaultDacl,
        TokenSource,
        TokenType,
        TokenImpersonationLevel,
        TokenStatistics,
        TokenRestrictedSids,
        TokenSessionId,
        TokenGroupsAndPrivileges,
        TokenSessionReference,
        TokenSandBoxInert,
        TokenAuditPolicy,
        TokenOrigin,
        TokenElevationType,
        TokenLinkedToken,
        TokenElevation,
        TokenHasRestrictions,
        TokenAccessInformation,
        TokenVirtualizationAllowed,
        TokenVirtualizationEnabled,
        TokenIntegrityLevel,
        TokenUIAccess,
        TokenMandatoryPolicy,
        TokenLogonSid,
        MaxTokenInfoClass  // MaxTokenInfoClass should always be the last enum 
    }

    public enum SECURITY_IMPERSONATION_LEVEL
    {
        SecurityAnonymous,
        SecurityIdentification,
        SecurityImpersonation,
        SecurityDelegation
    }


    public static bool IsAdmin()
    {
        var identity = WindowsIdentity.GetCurrent();
        return (null != identity && new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator));
    }

    /// <summary>
    /// The function checks whether the primary access token of the process belongs
    /// to user account that is a member of the local Administrators group, even if
    /// it currently is not elevated.
    /// </summary>
    /// <returns>
    /// Returns true if the primary access token of the process belongs to user
    /// account that is a member of the local Administrators group. Returns false
    /// if the token does not.
    /// </returns>
    public static bool CanBeAdmin()
    {
        bool fInAdminGroup = false;
        IntPtr hToken = IntPtr.Zero;
        IntPtr hTokenToCheck = IntPtr.Zero;
        IntPtr pElevationType = IntPtr.Zero;
        IntPtr pLinkedToken = IntPtr.Zero;
        int cbSize = 0;

        if (IsAdmin())
            return true;

        try
        {
            // Check the token for this user
            hToken = WindowsIdentity.GetCurrent().Token;

            // Determine whether system is running Windows Vista or later operating
            // systems (major version >= 6) because they support linked tokens, but
            // previous versions (major version < 6) do not.
            if (Environment.OSVersion.Version.Major >= 6)
            {
                // Running Windows Vista or later (major version >= 6).
                // Determine token type: limited, elevated, or default.

                // Allocate a buffer for the elevation type information.
                cbSize = sizeof(TOKEN_ELEVATION_TYPE);
                pElevationType = Marshal.AllocHGlobal(cbSize);
                if (pElevationType == IntPtr.Zero)
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                // Retrieve token elevation type information.
                if (!GetTokenInformation(hToken,
                    TOKEN_INFORMATION_CLASS.TokenElevationType, pElevationType, cbSize, out cbSize))
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                // Marshal the TOKEN_ELEVATION_TYPE enum from native to .NET.
                TOKEN_ELEVATION_TYPE elevType = (TOKEN_ELEVATION_TYPE)Marshal.ReadInt32(pElevationType);

                // If limited, get the linked elevated token for further check.
                if (elevType == TOKEN_ELEVATION_TYPE.TokenElevationTypeLimited)
                {
                    // Allocate a buffer for the linked token.
                    cbSize = IntPtr.Size;
                    pLinkedToken = Marshal.AllocHGlobal(cbSize);
                    if (pLinkedToken == IntPtr.Zero)
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }

                    // Get the linked token.
                    if (!GetTokenInformation(hToken,
                        TOKEN_INFORMATION_CLASS.TokenLinkedToken, pLinkedToken,
                        cbSize, out cbSize))
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }

                    // Marshal the linked token value from native to .NET.
                    hTokenToCheck = Marshal.ReadIntPtr(pLinkedToken);
                }
            }

            // CheckTokenMembership requires an impersonation token. If we just got
            // a linked token, it already is an impersonation token.  If we did not
            // get a linked token, duplicate the original into an impersonation
            // token for CheckTokenMembership.
            if (hTokenToCheck == IntPtr.Zero)
            {
                if (!DuplicateToken(hToken, (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, ref hTokenToCheck))
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }

            // Check if the token to be checked contains admin SID.
            WindowsIdentity id = new WindowsIdentity(hTokenToCheck);
            WindowsPrincipal principal = new WindowsPrincipal(id);
            fInAdminGroup = principal.IsInRole(WindowsBuiltInRole.Administrator);
        }
        catch
        {
            return false;
        }
        finally
        {
            // Centralized cleanup for all allocated resources.
            if (pElevationType != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(pElevationType);
                pElevationType = IntPtr.Zero;
            }
            if (pLinkedToken != IntPtr.Zero)
            {
                Marshal.FreeHGlobal(pLinkedToken);
                pLinkedToken = IntPtr.Zero;
            }
        }

        return fInAdminGroup;
    }

Это адаптировано из статьи, которую я где-то нашел в Интернете, извините, потерял атрибуцию.

7 голосов
/ 04 мая 2011

Это сработало для меня - все, что мне было нужно, это проверить, была ли программа запущена в роли администратора:

   public static bool IsAdminRole()
    {
        AppDomain domain = Thread.GetDomain();

        domain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
        WindowsPrincipal principle = (WindowsPrincipal)Thread.CurrentPrincipal;
        return principle.IsInRole(WindowsBuiltInRole.Administrator);
    }

Надеюсь, кто-то найдет это полезным!

Mike

3 голосов
/ 14 августа 2011

Я нашел еще одну статью здесь о stackoverflow, которая занимается этим по-другому. Я адаптировал это в метод ниже. При использовании Windows 7 это возвращало true для администраторов, false для не-администраторов и true для не-администраторов при «Запуск от имени администратора». Похоже, это будет работать только с .Net 3.5 и XP SP2 и более поздними версиями, основываясь на первоначальном взгляде на MSDN для класса PrincipleContext.

private static bool IsUserAdmin()
{
    bool isAdmin = false;

    WindowsIdentity wi = WindowsIdentity.GetCurrent();
    WindowsPrincipal wp = new WindowsPrincipal(wi);
    isAdmin = wp.IsInRole(WindowsBuiltInRole.Administrator);

    Console.WriteLine(isAdmin); // False for Windows 7 even if user is admin

    //found the code below at [http://stackoverflow.com/questions/1089046/in-net-c-test-if-user-is-an-administrative-user][1]  

    // Add reference to System.DirectoryServices.AccountManagement (Add Referemce -> .Net)
    // Add using System.DirectoryServices.AccountManagement;

    if (!isAdmin) //PrincipleContext takes a couple seconds, so I don't use it if not necessary
    {
        using (PrincipalContext pc = new PrincipalContext(ContextType.Machine, null))
        {
            UserPrincipal up = UserPrincipal.Current;
            GroupPrincipal gp = GroupPrincipal.FindByIdentity(pc, "Administrators");
            if (up.IsMemberOf(gp))
            {
                isAdmin = true;
            }
        }
    }
    Console.WriteLine(isAdmin); // True for Windows 7 if user is admin


    return isAdmin;
}
1 голос
/ 20 апреля 2010

Ваша заявка не повышена. В обычных условиях UAC удаляет «администраторские права» пользователя. Если приложение может использоваться только администраторами, добавьте манифест, который заставляет его подняться, чтобы они могли сохранять свои права администратора. Если он может использоваться любым из них, лучше всего разделить его на две части, одну с возвышенным манифестом, а другую без, и запустить приподнятую часть из кнопки или пункта меню, который украшен щитом, чтобы пользователи не нажимали это если они не администраторы. (В старых ОС сообщение о надевании экрана на кнопку будет игнорироваться.) Поиск по «UAC», «partition» и «shellexecute» будет полезен.

0 голосов
/ 25 сентября 2013

Я использовал тот же подход, что и DavB.cs: http://tieledeclercq.blogspot.be/2013/09/c-is-this-valid-administrator-that-can.html

С некоторыми отличиями:

  1. Администратор может быть вложенным членом группы локальных администраторов.
  2. Мне нужно было использовать внешние учетные данные (не как текущий пользователь).
...