2010-04-20 59 views
8

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

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

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

这工作,如果当前用户是Administrator用户,或者是说是Builtin\Administrators组的成员另一个用户。

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

什么可能导致这种差异?我有一种直觉,认为默认设置已经改变了某处(gpedit可能),但找不到任何看起来像罪魁祸首的东西。

+0

问完这个问题之后,我有一些手动检查组成员并在Vista机器上复制行为的代码。然后,我就意识到UAC是个问题 - 在这里运行Vista的大多数机器都关闭了UAC。 应用程序检查从另一个源定义的组的成员资格,因此它可能会因应用程序是否需要以管理员身份运行而不同 - 清单文件太过苛刻。我认为改变应用程序中的默认组可能是最简单的解决方案。 – adrianbanks 2010-04-21 11:25:21

回答

9

问题是,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; 
    } 

它改编自我在网上找到的一篇文章,对不起,失去了归属。

+0

更好的选择可能是重新考虑您的设计,以便它不需要用户成为管理员。也许你使用某种Windows服务具有管理权限。问题是,使用ASP应用程序时,您向Web用户公开了管理权限,如果代码中存在允许人们执行arbibary代码的缺陷,则这可能会很糟糕。 – 2010-04-20 18:49:47

+0

没有办法避免它。但是你是对的,将需要管理员权限的代码包装在“提升/撤销”中是一个好主意,所以没有曝光。 (这是我做的) – 2010-04-20 23:02:37

+0

非常感谢你,史蒂夫! – 2010-11-25 14:13:52

1

您的应用程序未被提升。在正常情况下,UAC将剥离用户的“管理员权限”。如果应用程序只能由管理员使用,请添加一个使其升级的清单,以便他们可以保留其管理权限。如果它可以被任何一个使用,最好的办法是分成两部分,一部分是升降清单,另一部分是没有的部分,然后从用盾牌装饰的按钮或菜单项启动升高的部分,以便用户不会点击如果他们不是管理员。 (在较旧的操作系统上,屏蔽该按钮的消息将被忽略。)在“UAC”,“分区”和“shellexecute”上搜索将会很有帮助。

7

这为我工作 - 所有我需要的是检查,如果方案已在管理角色已经启动:

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

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

希望有人认为,使用!

迈克

+0

很好的回答,使用它! – 2015-05-20 15:52:15

3

我发现这里计算器上的另一篇文章从而​​能够解决这个法子。 我改编成下面的方法。 使用Windows 7时,管理员返回true,非管理员返回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; 
}