Получение информации о пользователях и группах домена

Получение информации о пользователях и группах домена

////////////////////////////////////////////////////////////////////////////////
//
// ****************************************************************************
// * Project : DomainInfo
// * Unit Name : uMain
// * Purpose : Демо получения информации о пользователях и группах домена
// * Author : Александр (Rouse_) Багель
// * Version : 1.00
// ****************************************************************************
//
// Спасибо милой девушке Ане и группе "Машина Времени" за моральную поддержку...
//
unit uMain;
interface
uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, ExtCtrls, ComCtrls
 {$IFDEF VER150}
  , XPMan
 {$ENDIF};
const
 netapi32lib = 'netapi32.dll';
 NERR_Success = NO_ERROR;
type
 // Структура для получения информации о рабочей станции
 PWkstaInfo100 = ^TWkstaInfo100;
 TWkstaInfo100 = record
  wki100_platform_id : DWORD;
  wki100_computername : PWideChar;
  wki100_langroup : PWideChar;
  wki100_ver_major : DWORD;
  wki100_ver_minor : DWORD;
 end;
 // Итруктура для определения DNS имени контролера домена
 TDomainControllerInfoA = record
  DomainControllerName: LPSTR;
  DomainControllerAddress: LPSTR;
  DomainControllerAddressType: ULONG;
  DomainGuid: TGUID;
  DomainName: LPSTR;
  DnsForestName: LPSTR;
  Flags: ULONG;
  DcSiteName: LPSTR;
  ClientSiteName: LPSTR;
 end;
 PDomainControllerInfoA = ^TDomainControllerInfoA;
 // Структура для отображения пользователей
 PNetDisplayUser = ^TNetDisplayUser;
 TNetDisplayUser = record
  usri1_name: LPWSTR;
  usri1_comment: LPWSTR;
  usri1_flags: DWORD;
  usri1_full_name: LPWSTR;
  usri1_user_id: DWORD;
  usri1_next_index: DWORD;
 end;
 // Структура для отображения рабочих станций
 PNetDisplayMachine = ^TNetDisplayMachine;
 TNetDisplayMachine = record
  usri2_name: LPWSTR;
  usri2_comment: LPWSTR;
  usri2_flags: DWORD;
  usri2_user_id: DWORD;
  usri2_next_index: DWORD;
 end;
 // Структура для отображения групп
 PNetDisplayGroup = ^TNetDisplayGroup;
 TNetDisplayGroup = record
  grpi3_name: LPWSTR;
  grpi3_comment: LPWSTR;
  grpi3_group_id: DWORD;
  grpi3_attributes: DWORD;
  grpi3_next_index: DWORD;
 end;
 // Структура для отображения пользователей принадлежащих группе
 // или групп в которые входит пользователь
 PGroupUsersInfo0 = ^TGroupUsersInfo0;
 TGroupUsersInfo0 = record
  grui0_name: LPWSTR;
 end;
 TfrmDomainInfo = class(TForm)
  Button1: TButton;
  gbCurrent: TGroupBox;
  gbDomainResList: TGroupBox;
  ledCompName: TLabeledEdit;
  ledUserName: TLabeledEdit;
  ledDomainName: TLabeledEdit;
  ledControllerName: TLabeledEdit;
  lvUsers: TListView;
  gbInfo: TGroupBox;
  lbInfo: TListBox;
  VSplitter: TSplitter;
  pcRes: TPageControl;
  TabSheet1: TTabSheet;
  TabSheet2: TTabSheet;
  TabSheet3: TTabSheet;
  lvWorkStation: TListView;
  lvGroups: TListView;
  Label1: TLabel;
  memTrustedDomains: TMemo;
  ledDNSName: TLabeledEdit;
  procedure FormCreate(Sender: TObject);
  procedure lvGroupsClick(Sender: TObject);
 private
  CurrentDomainName: String;
  function GetCurrentUserName: String;
  function GetCurrentComputerName: String;
  function GetDomainController(const DomainName: String): String;
  function GetDNSDomainName(const DomainName: String): String;
  function EnumAllTrustedDomains: Boolean;
  function EnumAllUsers: Boolean;
  function EnumAllGroups: Boolean;
  function EnumAllWorkStation: Boolean;
  function GetSID(const SecureObject: String): String;
  function GetAllGroupUsers(const GroupName: String): Boolean;
  function GetAllUserGroups(const UserName: String): Boolean;
 end;
 // Функции которые предоставят нам возможность получения информации
 function NetApiBufferFree(Buffer: Pointer): DWORD; stdcall;
  external netapi32lib;
 function NetWkstaGetInfo(ServerName: PWideChar; Level: DWORD;
  Bufptr: Pointer): DWORD; stdcall; external netapi32lib;
 function NetGetDCName(ServerName: PWideChar; DomainName: PWideChar;
  var Bufptr: PWideChar): DWORD; stdcall; external netapi32lib;
 function DsGetDcName(ComputerName, DomainName: PChar; DomainGuid: PGUID;
  SiteName: PChar; Flags: ULONG;
  var DomainControllerInfo: PDomainControllerInfoA): DWORD; stdcall;
  external netapi32lib name 'DsGetDcNameA';
 function NetQueryDisplayInformation(ServerName: PWideChar; Level: DWORD;
  Index: DWORD; EntriesRequested: DWORD; PreferredMaximumLength: DWORD;
  var ReturnedEntryCount: DWORD; SortedBuffer: Pointer): DWORD; stdcall;
  external netapi32lib;
 function NetGroupGetUsers(ServerName: PWideChar; GroupName: PWideChar; Level: DWORD;
  var Bufptr: Pointer; PrefMaxLen: DWORD; var EntriesRead: DWORD;
  var TotalEntries: DWORD; ResumeHandle: PDWORD): DWORD; stdcall;
  external netapi32lib;
 function NetUserGetGroups(ServerName: PWideChar; UserName: PWideChar; Level: DWORD;
  var Bufptr: Pointer; PrefMaxLen: DWORD; var EntriesRead: DWORD;
  var TotalEntries: DWORD): DWORD; stdcall; external netapi32lib;
 function NetEnumerateTrustedDomains(ServerName: PWideChar;
  DomainNames: PWideChar): DWORD; stdcall; external netapi32lib;
 procedure ConvertSidToStringSid(SID: PSID; var StringSid: LPSTR); stdcall;
  external advapi32 name 'ConvertSidToStringSidA';
var
 frmDomainInfo: TfrmDomainInfo;
implementation
{$R *.dfm}
// Данная функция получает информацию о всех группах присутствующих в домене
// =============================================================================
function TfrmDomainInfo.EnumAllGroups: Boolean;
var
 Tmp, Info: PNetDisplayGroup;
 I, CurrIndex, EntriesRequest,
 PreferredMaximumLength,
 ReturnedEntryCount: Cardinal;
 Error: DWORD;
begin
 CurrIndex := 0;
 repeat
  Info := nil;
  // NetQueryDisplayInformation возвращает информацию только о 100-а записях
  // для того чтобы получить всю информацию используется третий параметр,
  // передаваемый функции, который определяет с какой записи продолжать
  // вывод информации
  EntriesRequest := 100;
  PreferredMaximumLength := EntriesRequest * SizeOf(TNetDisplayGroup);
  ReturnedEntryCount := 0;
  // Для выполнения функции, в нее нужно передать DNS имя контролера домена
  // (или его IP адрес), с которого мы хочем получить информацию
  // Для получения информации о группах используется структура NetDisplayGroup
  // и ее идентификатор 3 (тройка) во втором параметре
  Error := NetQueryDisplayInformation(StringToOleStr(ledControllerName.Text), 3, CurrIndex,
  EntriesRequest, PreferredMaximumLength, ReturnedEntryCount, @Info);
  // При безошибочном выполнении фунции будет результат либо
  // 1. NERR_Success - все записи возвращены
  // 2. ERROR_MORE_DATA - записи возвращены, но остались еще и нужно вызывать функцию повторно
  if Error in [NERR_Success, ERROR_MORE_DATA] then
  try
  Tmp := Info;
  // Выводим информацию которую вернула функция в структуру
  for I := 0 to ReturnedEntryCount - 1 do
  begin
  with lvGroups.Items.Add do
  begin
  Caption := Tmp^.grpi3_name; // Имя группы
  SubItems.Add(Tmp^.grpi3_comment); // Комментарий
  SubItems.Add(GetSID(Caption)); // SID группы
  // Запоминаем индекс с которым будем вызывать повторно функцию (если нужно)
  CurrIndex := Tmp^.grpi3_next_index;
  end;
  Inc(Tmp);
  end;
  finally
  // Чтобы небыло утечки ресурсов, освобождаем память занятую функцией под структуру
  NetApiBufferFree(Info);
  end;
 // Если результат выполнения функции ERROR_MORE_DATA - вызываем функцию повторно
 until Error in [NERR_Success, ERROR_ACCESS_DENIED];
 // Ну и возвращаем результат всего что мы тут накодили
 Result := Error = NERR_Success;
end;
// Данная функция получает информацию о всех доверенных доменах
// =============================================================================
function TfrmDomainInfo.EnumAllTrustedDomains: Boolean;
var
 Tmp, DomainList: PWideChar;
begin
 // Используем недокументированную функцию NetEnumerateTrustedDomains
 // (только не пойму, с какого перепуга она не документирована?)
 // Тут все очень просто, на вход имя контролера домена, ны выход - список доверенных доменов
 Result := NetEnumerateTrustedDomains(StringToOleStr(ledControllerName.Text),
  @DomainList) = NERR_Success;
 // Если вызов функции успешен, то...
 if Result then
 try
  Tmp := DomainList;
  while Length(Tmp) > 0 do
  begin
  memTrustedDomains.Lines.Add(Tmp); // Банально выводим список на экран
  Tmp := Tmp + Length(Tmp) + 1;
  end;
 finally
  // Не забываем про память
  NetApiBufferFree(DomainList);
 end;
end;
// Данная функция получает информацию о всех пользователях присутствующих в домене
// =============================================================================
function TfrmDomainInfo.EnumAllUsers: Boolean;
var
 Tmp, Info: PNetDisplayUser;
 I, CurrIndex, EntriesRequest,
 PreferredMaximumLength,
 ReturnedEntryCount: Cardinal;
 Error: DWORD;
begin
 CurrIndex := 0;
 repeat
  Info := nil;
  // NetQueryDisplayInformation возвращает информацию только о 100-а записях
  // для того чтобы получить всю информацию используется третий параметр,
  // передаваемый функции, который определяет с какой записи продолжать
  // вывод информации
  EntriesRequest := 100;
  PreferredMaximumLength := EntriesRequest * SizeOf(TNetDisplayUser);
  ReturnedEntryCount := 0;
  // Для выполнения функции, в нее нужно передать DNS имя контролера домена
  // (или его IP адрес), с которого мы хочем получить информацию
  // Для получения информации о пользователях используется структура NetDisplayUser
  // и ее идентификатор 1 (единица) во втором параметре
  Error := NetQueryDisplayInformation(StringToOleStr(ledControllerName.Text), 1, CurrIndex,
  EntriesRequest, PreferredMaximumLength, ReturnedEntryCount, @Info);
  // При безошибочном выполнении фунции будет результат либо
  // 1. NERR_Success - все записи возвращены
  // 2. ERROR_MORE_DATA - записи возвращены, но остались еще и нужно вызывать функцию повторно
  if Error in [NERR_Success, ERROR_MORE_DATA] then
  try
  Tmp := Info;
  // Выводим информацию которую вернула функция в структуру
  for I := 0 to ReturnedEntryCount - 1 do
  begin
  with lvUsers.Items.Add do
  begin
  Caption := Tmp^.usri1_name; // Имя пользователя
  SubItems.Add(Tmp^.usri1_comment); // Комментарий
  SubItems.Add(GetSID(Caption)); // Его SID
  // Запоминаем индекс с которым будем вызывать повторно функцию (если нужно)
  CurrIndex := Tmp^.usri1_next_index;
  end;
  Inc(Tmp);
  end;
  finally
  // Грохаем выделенную при вызове NetQueryDisplayInformation память
  NetApiBufferFree(Info);
  end;
 // Если результат выполнения функции ERROR_MORE_DATA
 // (т.е. есть еще данные) - вызываем функцию повторно
 until Error in [NERR_Success, ERROR_ACCESS_DENIED];
 // Ну и возвращаем результат всего что мы тут накодили
 Result := Error = NERR_Success;
end;
// Данная функция получает информацию о всех рабочих станциях присутствующих в домене
// Вообщето так делать немного не верно, дело в том что рабочие станции могут
// присутствовать в списке не только те, которые завел сисадмин (но для демки сойдет и так)
// =============================================================================
function TfrmDomainInfo.EnumAllWorkStation: Boolean;
var
 Tmp, Info: PNetDisplayMachine;
 I, CurrIndex, EntriesRequest,
 PreferredMaximumLength,
 ReturnedEntryCount: Cardinal;
 Error: DWORD;
begin
 CurrIndex := 0;
 repeat
  Info := nil;
  // NetQueryDisplayInformation возвращает информацию только о 100-а записях
  // для того чтобы получить всю информацию используется третий параметр,
  // передаваемый функции, который определяет с какой записи продолжать
  // вывод информации
  EntriesRequest := 100;
  PreferredMaximumLength := EntriesRequest * SizeOf(TNetDisplayMachine);
  ReturnedEntryCount := 0;
  // Для выполнения функции, в нее нужно передать DNS имя контролера домена
  // (или его IP адрес), с которого мы хочем получить информацию
  // Для получения информации о рабочих станциях используется структура NetDisplayMachine
  // и ее идентификатор 2 (двойка) во втором параметре
  Error := NetQueryDisplayInformation(StringToOleStr(ledControllerName.Text), 2, CurrIndex,
  EntriesRequest, PreferredMaximumLength, ReturnedEntryCount, @Info);
  // При безошибочном выполнении фунции будет результат либо
  // 1. NERR_Success - все записи возвращены
  // 2. ERROR_MORE_DATA - записи возвращены, но остались еще и нужно вызывать функцию повторно
  if Error in [NERR_Success, ERROR_MORE_DATA] then
  try
  Tmp := Info;
  // Выводим информацию которую вернула функция в структуру
  for I := 0 to ReturnedEntryCount - 1 do
  begin
  with lvWorkStation.Items.Add do
  begin
  Caption := Tmp^.usri2_name; // Имя рабочей станции
  SubItems.Add(Tmp^.usri2_comment); // Комментарий
  SubItems.Add(GetSID(Caption)); // Её SID
  // Запоминаем индекс с которым будем вызывать повторно функцию (если нужно)
  CurrIndex := Tmp^.usri2_next_index;
  end;
  Inc(Tmp);
  end;
  finally
  // Дабы небыло утечек
  NetApiBufferFree(Info);
  end;
 // Если результат выполнения функции ERROR_MORE_DATA
 // (т.е. есть еще данные) - вызываем функцию повторно
 until Error in [NERR_Success, ERROR_ACCESS_DENIED];
 // Ну и возвращаем результат всего что мы тут накодили
 Result := Error = NERR_Success;
end;
procedure TfrmDomainInfo.FormCreate(Sender: TObject);
begin
 // Просто вызываем все функции подряд (не делал проверок на результат функций)
 ledUserName.Text := GetCurrentUserName;
 ledCompName.Text := GetCurrentComputerName;
 ledDomainName.Text := CurrentDomainName;
 ledControllerName.Text := GetDomainController(CurrentDomainName);
 // Единственно, если нет контролера домена, то дальше определять бесполезно
 if ledControllerName.Text = '' then Exit;
 ledDNSName.Text := GetDNSDomainName(CurrentDomainName);
 EnumAllTrustedDomains;
 EnumAllUsers;
 EnumAllWorkStation;
 EnumAllGroups;
end;
// Довольно простая функция, возвращает только имена пользователей принадлезжащих группе
// =============================================================================
function TfrmDomainInfo.GetAllGroupUsers(const GroupName: String): Boolean;
var
 Tmp, Info: PGroupUsersInfo0;
 PrefMaxLen, EntriesRead,
 TotalEntries, ResumeHandle: DWORD;
 I: Integer;
begin
 // На вход подается список который мы будем заполнять
 lbInfo.Items.Clear;
 // Обязательная инициализация
 ResumeHandle := 0;
 PrefMaxLen := DWORD(-1);
 // Выполняем
 Result := NetGroupGetUsers(StringToOleStr(ledControllerName.Text),
  StringToOleStr(GroupName), 0, Pointer(Info), PrefMaxLen,
  EntriesRead, TotalEntries, @ResumeHandle) = NERR_Success;
 // Смотрим результат...
 if Result then
 try
  Tmp := Info;
  for I := 0 to EntriesRead - 1 do
  begin
  lbInfo.Items.Add(Tmp^.grui0_name); // Банально выводим результат из структуры
  Inc(Tmp);
  end;
 finally
  // Не забываем, ибо может быть склероз :)
  NetApiBufferFree(Info);
 end;
end;
// Аналогично предыдущей функции (заметьте - структура таже)
// =============================================================================
function TfrmDomainInfo.GetAllUserGroups(const UserName: String): Boolean;
var
 Tmp, Info: PGroupUsersInfo0;
 PrefMaxLen, EntriesRead,
 TotalEntries: DWORD;
 I: Integer;
begin
 lbInfo.Items.Clear;
 PrefMaxLen := DWORD(-1);
 Result := NetUserGetGroups(StringToOleStr(ledControllerName.Text),
  StringToOleStr(UserName), 0, Pointer(Info), PrefMaxLen,
  EntriesRead, TotalEntries) = NERR_Success;
 if Result then
 try
  Tmp := Info;
  for I := 0 to EntriesRead - 1 do
  begin
  lbInfo.Items.Add(Tmp^.grui0_name);
  Inc(Tmp);
  end;
 finally
  NetApiBufferFree(Info);
 end;
end;
// Получаем имя компьютера и имя домена
// =============================================================================
function TfrmDomainInfo.GetCurrentComputerName: String;
var
 Info: PWkstaInfo100;
 Error: DWORD;
begin
 // А для этого мы воспользуемся следующей функцией
 Error := NetWkstaGetInfo(nil, 100, @Info);
 if Error <> 0 then
  raise Exception.Create(SysErrorMessage(Error));
 // Как видно, вызов который возвращает обычную структуру, из которой и прочитаем, все что нужно :)
 // А именно имя компьютера в сети
 Result := Info^.wki100_computername;
 // И где он находиться
 CurrentDomainName := info^.wki100_langroup;
end;
// Без комментариев
// =============================================================================
function TfrmDomainInfo.GetCurrentUserName: String;
var
 Size: Cardinal;
begin
 Size := MAXCHAR;
 SetLength(Result, Size);
 GetUserName(PChar(Result), Size);
 SetLength(Result, Size);
end;
// Получаем DNS имя контроллера домена
// =============================================================================
function TfrmDomainInfo.GetDNSDomainName(const DomainName: String): String;
const
 DS_IS_FLAT_NAME = $00010000;
 DS_RETURN_DNS_NAME = $40000000;
var
 GUID: PGUID;
 DomainControllerInfo: PDomainControllerInfoA;
begin
 GUID := nil;
 // Для большинства операций нам потребуется IP адрес контроллера домена
 // или его DNS имя, которое мы получим вот так:
 if DsGetDcName(nil, PChar(CurrentDomainName), GUID, nil,
  DS_IS_FLAT_NAME or DS_RETURN_DNS_NAME, DomainControllerInfo) = NERR_Success then
 // Параметры которые мы передаем означают:
 // DS_IS_FLAT_NAME - передаем просто имя домена
 // DS_RETURN_DNS_NAME - ждем получения DNS имени
 try
  Result := DomainControllerInfo^.DomainControllerName; // Результат собсно тут...
 finally
  // Склероз это болезнь, ее нужно лечить...
  NetApiBufferFree(DomainControllerInfo);
 end;
end;
// Ну тут без комментариев - просто получаем имя контроллера домена
// =============================================================================
function TfrmDomainInfo.GetDomainController(const DomainName: String): String;
var
 Domain: WideString;
 Server: PWideChar;
begin
 Domain := StringToOleStr(DomainName);
 if NetGetDCName(nil, @Domain[1], Server) = NERR_Success then
 try
  Result := Server;
 finally
  NetApiBufferFree(Server);
 end;
end;
// Не знаю зачем добавил это, ну раз добавил - получение SID объекта
// Без комментариев...
// =============================================================================
function TfrmDomainInfo.GetSID(const SecureObject: String): String;
var
 SID: PSID;
 StringSid: PChar;
 ReferencedDomain: String;
 cbSid, cbReferencedDomain:DWORD;
 peUse: SID_NAME_USE;
begin
 cbSID := 128;
 cbReferencedDomain := 16;
 GetMem(SID, cbSid);
 try
  SetLength(ReferencedDomain, cbReferencedDomain);
  if LookupAccountName(PChar(ledDNSName.Text),
  PChar(SecureObject), SID, cbSid,
  @ReferencedDomain[1], cbReferencedDomain, peUse) then
  begin
  ConvertSidToStringSid(SID, StringSid);
  Result := StringSid;
  end;
 finally
  FreeMem(SID);
 end;
end;
procedure TfrmDomainInfo.lvGroupsClick(Sender: TObject);
var
 Value: String;
begin
 if (Sender as TListView).Selected = nil then Exit;
 Value := (Sender as TListView).Selected.Caption;
 case (Sender as TListView).Tag of
  0:
  begin
  gbInfo.Caption := Format('Группы в которые входит пользователь "%s"', [Value]);
  GetAllUserGroups(Value);
  end;
  1:
  begin
  gbInfo.Caption := Format('Группы в которые входит рабочая станция "%s"', [Value]);
  GetAllUserGroups(Value);
  end;
  2:
  begin
  gbInfo.Caption := Format('Объекты входящие в группу "%s"', [Value]);
  GetAllGroupUsers(Value);
  end;
 end;
end;
end.
Проект также доступен по адресу: http://rouse.front.ru/domaininfo.zip Взято из http://forum.sources.ru Автор: Rouse_

Отправить комментарий

Проверка
Антиспам проверка
Image CAPTCHA
...