使用C#枚举远程系统上的Windows用户组成员
在C#中,我需要能够
- 连接到远程系统,并根据需要指定用户名/密码
- 列出该系统上本地组的成员
- 将结果取回执行计算机
因此,例如,我将使用适当的凭据连接到\ SOMESYSTEM,然后取回本地管理员的列表,包括SOMESYSTEM \ Administrator,SOMESYSTEM \ Bob,DOMAIN \ AlanH," DOMAIN \ Domain Administrators"。
我已经尝试过使用system.directoryservices.accountmanagement进行此操作,但是遇到身份验证问题。有时我得到:
不允许同一用户使用多个用户名与服务器或者共享资源建立多个连接。断开与服务器或者共享资源的所有先前连接,然后重试。 (来自HRESULT的异常:0x800704C3)
进行上述操作是因为在某些情况下,我根本无法取消映射现有驱动器或者UNC连接的映射。
其他时候,我的程序遇到未知错误,并且远程系统上的安全日志报告错误675,代码0x19,即KDC_ERR_PREAUTH_REQUIRED。
我需要一种更简单,更不易出错的方式来做到这一点!
解决方案
回答
我们应该能够使用System.DirectoryServices.DirectoryEntry做到这一点。如果我们无法远程运行它,也许可以在远程计算机上安装一些东西,以通过某种RPC(例如远程处理或者Web服务)为我们提供数据。但是我认为我们尝试的内容应该可以远程实现而不会太花哨。
回答
如果Windows不允许我们通过其登录机制进行连接,我认为我们唯一的选择是在具有开放端口的远程计算机上运行某些内容(如前所述,直接或者通过远程处理或者Web服务)。
回答
使用WMI应该很容易做到这一点。在这里,我们可以找到一些文档的指针:
Win32_UserAccount的WMI文档
即使我们以前没有使用WMI的经验,也很容易将页面底部的VB脚本代码转换为某些.NET代码。
希望这对我们有所帮助!
回答
我建议使用Win32 API函数NetLocalGroupGetMembers。这比尝试找出疯狂的LDAP语法要简单得多,这对于此处建议的其他一些解决方案是必需的。只要我们通过调用" LoginUser"模拟要运行检查的用户,就不会遇到任何安全问题。
我们可以在此处找到用于模拟的示例代码。
如果我们需要帮助弄清楚如何从C#调用" NetLocalGroupGetMembers",我建议我们签出Jared Parson的PInvoke助手,可以从codeplex下载。
如果我们在IIS中运行的ASP.NET应用程序中运行代码,并且想要模拟访问该网站的用户以进行调用,则可能需要向生产Web服务器授予"受委托的委托"权限。
如果我们在桌面上运行,那么使用活动用户的安全凭据应该不会有问题。
网络管理员可能已经撤消了我们尝试访问的特定计算机对"安全对象"的访问。不幸的是,访问是所有网络管理api函数正常工作所必需的。如果是这种情况,那么我们将需要为要作为其执行身份的任何用户授予对"安全对象"的访问权限。但是,使用默认的Windows安全设置,所有经过身份验证的用户都应该具有访问权限。
我希望这有帮助。
-斯科特
回答
大卫走在正确的道路上,我相信他的答案。
但是必需的WMI查询比直接查询少了一点,因为我不仅需要整个计算机的用户列表,还需要是本地Administrators组成员的用户和组的子集,无论是本地的还是域的。作为记录,该WMI查询为:
SELECT PartComponent FROM Win32_GroupUser WHERE GroupComponent = "Win32_Group.Domain='thehostname',Name='thegroupname'"
这是完整的代码段:
public string GroupMembers(string targethost, string groupname, string targetusername, string targetpassword) { StringBuilder result = new StringBuilder(); try { ConnectionOptions Conn = new ConnectionOptions(); if (targethost != Environment.MachineName) //WMI errors if creds given for localhost { Conn.Username = targetusername; //can be null Conn.Password = targetpassword; //can be null } Conn.Timeout = TimeSpan.FromSeconds(2); ManagementScope scope = new ManagementScope("\\" + targethost + "\root\cimv2", Conn); scope.Connect(); StringBuilder qs = new StringBuilder(); qs.Append("SELECT PartComponent FROM Win32_GroupUser WHERE GroupComponent = \"Win32_Group.Domain='"); qs.Append(targethost); qs.Append("',Name='"); qs.Append(groupname); qs.AppendLine("'\""); ObjectQuery query = new ObjectQuery(qs.ToString()); ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query); ManagementObjectCollection queryCollection = searcher.Get(); foreach (ManagementObject m in queryCollection) { ManagementPath path = new ManagementPath(m["PartComponent"].ToString()); { String[] names = path.RelativePath.Split(','); result.Append(names[0].Substring(names[0].IndexOf("=") + 1).Replace("\"", " ").Trim() + "\"); result.AppendLine(names[1].Substring(names[1].IndexOf("=") + 1).Replace("\"", " ").Trim()); } } return result.ToString(); } catch (Exception e) { Console.WriteLine("Error. Message: " + e.Message); return "fail"; } }
因此,如果我调用Groupmembers(" Server1"," Administrators"," myusername"," mypassword");我得到一个字符串返回:
SERVER1 \管理员
MYDOMAIN \ Domain管理员
实际的WMI回报更像这样:
\ SERVER1 \ root \ cimv2:Win32_UserAccount.Domain =" SERVER1",Name =" Administrator"
...正如我们所看到的,我必须进行一些字符串操作才能使其美观。