Перечислять членов группы пользователей Windows на удаленной системе, используя c # - PullRequest
5 голосов
/ 22 августа 2008

В c # мне нужно уметь

  • Подключиться к удаленной системе, указав при необходимости имя пользователя / пароль
  • Список членов локальной группы в этой системе
  • Получить результаты обратно на исполняющий компьютер

Так, например, я бы подключился к \ SOMESYSTEM с соответствующими кредитами и получил список локальных администраторов, включая SOMESYSTEM \ Administrator, SOMESYSTEM \ Bob, DOMAIN \ AlanH, "DOMAIN \ Администраторы домена".

Я пробовал это с system.directoryservices.accountmanagement, но у меня проблемы с аутентификацией. Иногда я получаю:

Несколько подключений к серверу или общему ресурсу одним и тем же пользователем с использованием нескольких имен пользователей не допускаются. Отключите все предыдущие подключения к серверу или общему ресурсу и повторите попытку. (Исключение из HRESULT: 0x800704C3)

Вышеприведенная попытка выполняется, потому что будут ситуации, когда я просто не могу разархивировать существующие диски или соединения UNC.

В других случаях моя программа получает НЕИЗВЕСТНУЮ ОШИБКУ, и журнал безопасности на удаленной системе сообщает об ошибке 675, код 0x19, который является KDC_ERR_PREAUTH_REQUIRED.

Мне нужен более простой и менее подверженный ошибкам способ сделать это!

Ответы [ 5 ]

2 голосов
/ 23 августа 2008

Давидг был на правильном пути, и я доверяю ему ответ.

Но необходимый 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";
            }
        }

Итак, если я вызову членов группы («Server1», «Administrators», «myusername», «mypassword»); Я получаю одну строку, возвращаемую с:

SERVER1 \ Administrator
MYDOMAIN \ Администраторы домена

Фактический возврат WMI больше похож на это:

\\ SERVER1 \ корень \ cimv2: Win32_UserAccount.Domain = "SERVER1", Name = "Администратор"

... так что, как вы можете видеть, мне пришлось сделать небольшую манипуляцию со строками, чтобы украсить его.

1 голос
/ 22 августа 2008

Я бы рекомендовал использовать функцию Win32 API NetLocalGroupGetMembers . Это гораздо проще, чем пытаться понять сумасшедший синтаксис LDAP, который необходим для некоторых других решений, рекомендуемых здесь. Пока вы выдаете себя за пользователя, для которого вы хотите выполнить проверку, вызывая «LoginUser», вы не должны сталкиваться с какими-либо проблемами безопасности.

Вы можете найти пример кода для выполнения олицетворения здесь .

Если вам нужна помощь в выяснении того, как вызывать «NetLocalGroupGetMembers» из C #, я рекомендую вам обратиться к помощнику Джареда Парсона PInvoke, которого вы можете загрузить из codeplex.

Если вы выполняете код в приложении ASP.NET, работающем в IIS, и хотите выдать себя за пользователя, обращающегося к веб-сайту, чтобы выполнить вызов, то вам может потребоваться предоставить разрешение «Доверенные для делегирования» на производство веб сервер.

Если вы работаете на рабочем столе, то использование учетных данных активного пользователя не должно быть проблемой.

Возможно, ваш сетевой администратор мог отозвать доступ к «Защищаемому объекту» для конкретной машины, к которой вы пытаетесь получить доступ. К сожалению, этот доступ необходим для работы всех функций управления сетью API . Если это так, то вам нужно предоставить доступ к «Защищаемому объекту» всем пользователям, которых вы хотите выполнить. Однако с настройками безопасности Windows по умолчанию доступ должны иметь все прошедшие проверку пользователи.

Надеюсь, это поможет.

-Скотт

1 голос
/ 22 августа 2008

Это должно быть легко сделать с помощью WMI. Здесь у вас есть указатель на некоторые документы:

Документация WMI для Win32_UserAccount

Даже если у вас нет опыта работы с WMI, довольно просто превратить этот код VB Script внизу страницы в какой-нибудь код .NET.

Надеюсь, это помогло!

0 голосов
/ 22 августа 2008

Если Windows не позволяет подключиться через механизм входа в систему, я думаю, что единственный вариант - запустить что-то на удаленной машине с открытым портом (напрямую или через удаленное взаимодействие или веб-службу, как уже упоминалось).

0 голосов
/ 22 августа 2008

Вы должны быть в состоянии сделать это с System.DirectoryServices.DirectoryEntry. Если у вас возникли проблемы с его удаленным запуском, возможно, вы могли бы установить что-то на удаленных компьютерах, чтобы предоставить вам данные с помощью какого-либо RPC, например удаленного взаимодействия или веб-службы. Но я думаю, что то, что вы пытаетесь сделать, должно быть возможным удаленно, не слишком прихотливо.

...