在 Windows 7 上调用 IPrincipal.IsInRole

发布于 2024-08-29 08:42:30 字数 923 浏览 13 评论 0 原文

我们在应用程序中使用 NTLM 身份验证来确定用户是否可以执行某些操作。我们使用当前 Windows 登录的 IPrincipal (在 WinForms 应用程序中),调用 IsInRole 检查特定组成员资格。

要检查用户是否是计算机上的本地管理员,我们使用:

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

如果当前用户是 Administrator 用户,或者是 Builtin\Administrators 成员的另一个用户,则此方法有效 组。

在 Windows 7 上的测试中,我们发现这不再按预期工作。 Administrator 用户仍然可以正常工作,但属于 Builtin\Administrators 组成员的任何其他用户都会为 IsInRole 调用返回 false。

是什么导致了这种差异?我有一种直觉,默认设置已经在某个地方发生了变化(可能在 gpedit 中),但找不到任何看起来像罪魁祸首的东西。

We use NTLM auth in our application to determine whether a user can perform certain operations. We use the IPrincipal of their current Windows login (in WinForms applications), calling IsInRole to check for specific group memberships.

To check that a user is a local administrator on the machine, we use:

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

This works if the current user is the Administrator user, or is another user that is a member of the Builtin\Administrators group.

In our testing on Windows 7, we have found that this no longer works as expected. The Administrator user still works fine, but any other user that is a member of the Builtin\Administrators group returns false for the IsInRole call.

What could be causing this difference? I have a gut feeling that a default setting has changed somewhere (possible in gpedit), but cannot find anything that looks like the culprit.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

云归处 2024-09-05 08:42:30

问题是 Windows 安全性(又名“UAC”)妨碍了您。对管理员角色进行了特殊处理,您的用户在升级之前实际上不会拥有这些角色。管理员角色在某种意义上是“幽灵”的:存在但无法进行权限检查,甚至无法(轻松)测试是否存在。请参阅以下位置的注释:
http://msdn.microsoft.com/en-us/library/46ks97y7.aspx

这是讨论该问题的系列,其中包含执行必要解决方法的示例代码:

我通过构建自己的 UAC 提示符并使用名称 & 解决了 ASP.NET 应用程序中的类似问题。调用 Win32 登录 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;
    }

它改编自我在网上找到的一篇文章,抱歉,丢失了出处。

The problem is that Windows security (aka "UAC") is getting in your way. There's special handling of administrator roles and your user will not actually have these roles until he is elevated. Admin roles are "ghosted" in a sense: present but unavailable for permission checks or even to (easily) test for presence. See the note at:
http://msdn.microsoft.com/en-us/library/46ks97y7.aspx

Here's a series that talks about the issue, with example code that does the necessary workarounds:

I solved a similar problem in an ASP.NET app by building my own UAC prompt and using the name & password to call the Win32 Logon API. You might be lucky enough to be in a .NET desktop app, in which case you can use regular elevation requests.

Here's some C# code to check admin permissions without elevating.

    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;
    }

It's adapted from an article I found online somewhere, sorry, lost the attribution.

深海蓝天 2024-09-05 08:42:30

这对我有用 - 我所需要的只是检查该程序是否已以管理员角色启动:

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

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

希望有人发现有用!

麦克风

This worked for me - all I needed was to check if the program had been started in an admin role:

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

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

Hope someone finds that of use!

Mike

墨洒年华 2024-09-05 08:42:30

我在 stackoverflow 上找到了另一篇文章,它以另一种方式解决了这个问题。
我把它改编成了下面的方法。
使用 Windows 7,当“以管理员身份运行”时,对于管理员返回 true,对于非管理员返回 false,对于非管理员返回 true。
根据对 PrincipleContext 类的 MSDN 的初步了解,这似乎仅适用于 .Net 3.5 和 XP SP2 及更高版本。

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;
}

I found another article here on stackoverflow which tackles this another way.
I adapted it into a method below.
Using Windows 7, this returned true for admins, false for non-admins, and true for non-admin when 'Run as administrator'.
It looks like this will only work with .Net 3.5 and XP SP2 and later, based on an initial glance at MSDN for the PrincipleContext class.

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;
}
一生独一 2024-09-05 08:42:30

你的申请没有提升。通常情况下,UAC 剥夺了用户的“管理员身份”。如果应用程序只能由管理员使用,请添加一个清单以使其提升,以便他们可以保持管理员权限。如果它可以被任何一个使用,那么最好的选择是将其分区为两个部分,一个具有提升清单,另一个没有,并从用盾牌装饰的按钮或菜单项启动提升的部分,这样用户就不会单击如果他们不是管理员的话。 (在较旧的操作系统上,将忽略将屏蔽放在按钮上的消息。)搜索“UAC”、“partition”和“shellexecute”将会有所帮助。

Your application is not elevated. Under normal circumstances UAC strips away the "administrator-ness" of the user. If the app can only be used by admins, add a manifest that causes it to elevate so they can keep their admin-ness. If it can be used by either, your best bet is to partition to two parts, one with an elevating manifest and one without, and launch the elevated part from a button or menu item that is decorated with the shield so users won't click it if they're not admins. (On older OS's the message to put the shield on the button will be ignored.) Searching on "UAC", "partition" and "shellexecute" will be helpful.

提笔落墨 2024-09-05 08:42:30

我使用了与 DavB.cs 相同的方法: http://tieledeclercq.blogspot.be/2013/09/c-is-this-valid-administrator-that-c​​an.html

有一些差异:

  1. 管理员可以是本地管理员组。
  2. 我需要使用外部凭据(而不是当前用户)。

I've used the same approach as DavB.cs: http://tieledeclercq.blogspot.be/2013/09/c-is-this-valid-administrator-that-can.html

With a few differences:

  1. The administrator could be a nested member of the local administrator group.
  2. I needed to use external credentials (not as the current user).
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文