使用c#枚举远程系统上的Windows用户组成员

发布于 2024-07-04 10:13:07 字数 587 浏览 15 评论 0原文

在 C# 中,我需要能够

  • 连接到远程系统,指定适当的用户名/密码
  • 列出该系统上本地组的成员
  • 将结果取回到执行计算机

因此例如我将使用适当的凭据连接到 \SOMESYSTEM ,并取回本地管理员列表,包括 SOMESYSTEM\Administrator、SOMESYSTEM\Bob、DOMAIN\AlanH、“DOMAIN\Domain Administrators”。

我已经尝试使用 system.directoryservices.accountmanagement 但遇到身份验证问题。 有时我会得到:

不允许同一用户使用多个用户名与服务器或共享资源进行多个连接。 断开之前与服务器或共享资源的所有连接,然后重试。 (HRESULT 异常:0x800704C3)

上面的尝试是因为在某些情况下我根本无法取消映射现有驱动器或 UNC 连接。

其他时候,我的程序遇到未知错误,并且远程系统上的安全日志报告错误 675,代码 0x19,即 KDC_ERR_PREAUTH_REQUIRED。

我需要一种更简单且不易出错的方法来做到这一点!

Within c#, I need to be able to

  • Connect to a remote system, specifying username/password as appropriate
  • List the members of a localgroup on that system
  • Fetch the results back to the executing computer

So for example I would connect to \SOMESYSTEM with appropriate creds, and fetch back a list of local administrators including SOMESYSTEM\Administrator, SOMESYSTEM\Bob, DOMAIN\AlanH, "DOMAIN\Domain Administrators".

I've tried this with system.directoryservices.accountmanagement but am running into problems with authentication. Sometimes I get:

Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again. (Exception from HRESULT: 0x800704C3)

The above is trying because there will be situations where I simply cannot unmap existing drives or UNC connections.

Other times my program gets UNKNOWN ERROR and the security log on the remote system reports an error 675, code 0x19 which is KDC_ERR_PREAUTH_REQUIRED.

I need a simpler and less error prone way to do this!

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

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

发布评论

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

评论(5

捂风挽笑 2024-07-11 10:13:08

使用 WMI 应该很容易做到这一点。 这里有一些文档的指针:

Win32_UserAccount 的 WMI 文档

如果您以前没有使用 WMI 的经验,那么将页面底部的 VB 脚本代码转换为一些 .NET 代码应该很容易。

希望这有帮助!

This should be easy to do using WMI. Here you have a pointer to some docs:

WMI Documentation for Win32_UserAccount

Even if you have no previous experience with WMI, it should be quite easy to turn that VB Script code at the bottom of the page into some .NET code.

Hope this helped!

绻影浮沉 2024-07-11 10:13:08

如果 Windows 不允许您通过其登录机制进行连接,我认为您唯一的选择是在具有开放端口的远程计算机上运行某些内容(直接或通过远程处理或 Web 服务,如上所述)。

If Windows won't let you connect through it's login mechanism, I think your only option is to run something on the remote machine with an open port (either directly or through remoting or a web service, as mentioned).

-柠檬树下少年和吉他 2024-07-11 10:13:08

我建议使用 Win32 API 函数 NetLocalGroupGetMembers。 这比尝试找出疯狂的 LDAP 语法要直接得多,而这对于此处推荐的其他一些解决方案来说是必需的。 只要您通过调用“LoginUser”来模拟要运行检查的用户,就不会遇到任何安全问题。

您可以在此处。

如果您需要帮助了解如何从 C# 调用“NetLocalGroupGetMembers”,我建议您查看 Jared Parson 的 PInvoke 助手,您可以 从 Codeplex 下载

如果您在 IIS 中运行的 ASP.NET 应用程序中运行代码,并且想要模拟访问网站的用户以便进行调用,那么您可能需要向生产 Web 服务器授予“受信任的委派”权限。

如果您在桌面上运行,那么使用活动用户的安全凭证应该不成问题。

您的网络管理员可能已撤销您尝试访问的特定计算机对“安全对象”的访问权限。 不幸的是,所有网络管理 api 功能都需要访问权限工作。 如果是这种情况,那么您将需要为您想要执行的任何用户授予对“安全对象”的访问权限。 但是,使用默认的 Windows 安全设置,所有经过身份验证的用户都应该具有访问权限。

我希望这有帮助。

-斯科特

I would recommend using the Win32 API function NetLocalGroupGetMembers. It is much more straight forward than trying to figure out the crazy LDAP syntax, which is necessary for some of the other solutions recommended here. As long as you impersonate the user you want to run the check as by calling "LoginUser", you should not run into any security issues.

You can find sample code for doing the impersonation here.

If you need help figuring out how to call "NetLocalGroupGetMembers" from C#, I reccomend that you checkout Jared Parson's PInvoke assistant, which you can download from codeplex.

If you are running the code in an ASP.NET app running in IIS, and want to impersonate the user accessing the website in order to make the call, then you may need to grant "Trusted for Delegation" permission to the production web server.

If you are running on the desktop, then using the active user's security credentials should not be a problem.

It is possible that you network admin could have revoked access to the "Securable Object" for the particular machine you are trying to access. Unfortunately that access is necessary for all of the network management api functions to work. If that is the case, then you will need to grant access to the "Securable Object" for whatever users you want to execute as. With the default windows security settings all authenticated users should have access, however.

I hope this helps.

-Scott

蘑菇王子 2024-07-11 10:13:08

您应该能够使用 System.DirectoryServices.DirectoryEntry 来执行此操作。 如果您在远程运行它时遇到问题,也许您可​​以在远程计算机上安装一些东西,通过某种 RPC(例如远程处理或 Web 服务)为您提供数据。 但我认为你正在尝试的事情应该可以远程实现,而不必太花哨。

You should be able to do this with System.DirectoryServices.DirectoryEntry. If you are having trouble running it remotely, maybe you could install something on the remote machines to give you your data via some sort of RPC, like remoting or a web service. But I think what you're trying should be possible remotely without getting too fancy.

于我来说 2024-07-11 10:13:08

大卫走在正确的道路上,我相信他的答案。

但所需的 WMI 查询有点不那么简单,因为我不仅需要整个计算机的用户列表,还需要用户和组的子集,无论是本地还是域,它们都是本地管理员组。 根据记录,该 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\Administrator
MYDOMAIN\Domain Admins

实际的 WMI 返回更像是这样:

\\SERVER1\root\cimv2:Win32_UserAccount.Domain="SERVER1",Name="Administrator"

...如您所见,我必须做一些字符串操纵来美化它。

davidg was on the right track, and I am crediting him with the answer.

But the WMI query necessary was a little less than straightfoward, since I needed not just a list of users for the whole machine, but the subset of users and groups, whether local or domain, that were members of the local Administrators group. For the record, that WMI query was:

SELECT PartComponent FROM Win32_GroupUser WHERE GroupComponent = "Win32_Group.Domain='thehostname',Name='thegroupname'"

Here's the full code snippet:

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

So, if I invoke Groupmembers("Server1", "Administrators", "myusername", "mypassword"); I get a single string returned with:

SERVER1\Administrator
MYDOMAIN\Domain Admins

The actual WMI return is more like this:

\\SERVER1\root\cimv2:Win32_UserAccount.Domain="SERVER1",Name="Administrator"

... so as you can see, I had to do a little string manipulation to pretty it up.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文