Рассылка закрыта
При закрытии подписчики были переданы в рассылку "Delphi - проблемы и решения" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
Введение в Security Identifiers (SID)
| Информационный Канал Subscribe.Ru |
Выпуск номер : 4 [ 30 июля 2002 года]
Здравствуйте, уважаемые подписчики.
По сравнению с прошлым выпуском, нас стало в несколько (десятков) раз больше :). Благодарю всех подписавшихся и напомню, что вы можете писать мне на flint@vtc.ru ваши вопросы, замечания и предложения (кроме спама :))
Сегодня в рассылке:
- Что такое SID
- Вопросы без ответов
Статья
Что такое SID
Сегодня мы разберем очень важное и часто используемое понятие - SID (Security Identifier) - идентификатор безопасности. Напомню, что в прошлом выпуске рассылки для добавления в локальную группу мы использовали SID, а не имя пользователя или группы.
Так что же такое SID? SID - это уникальное двоичное представление доверенного объекта (пользователя, группы, компьютера, домена и т.д.). Т.е. каждому объекту сопоставлено уникальное (шанс совпадения очень мал) число (пусть пока будет число, о структуре SID ниже), по которому (а вовсе не по имени) система и опознает объект.
Подготавливая этот выпуск, я решил посмотреть, что же интересного по SID есть в интернете. Яndex выдал кучу одинаковых ссылок, половина из которых вела на FAQ с вопросом "Как узнать SID юзера?", а вторая - "Словарь компьютерных терминов". В этом словаре написано следующее : "SID - содержит информацию о том, к каким группам принадлежит пользователь и какими привилегиями обладает.". Это в корне не верно. Опровергнуть это утверждение может хотя бы тот факт, что SID для определенного объекта постоянный (т.е. при перемещении в группу он не меняется :).
Теперь давайте проведем эксперимент - создадим пользователя (которого потом удалим) с именем test и любой файлик (если у вас не NTFS, то файл создавать бесполезно. Но можно заменить его созданием раздела в реестре, лучше в HKEY_CURRENT_USER\Software). Теперь смотрим свойства файла -> Безопасность (для реестра запустите regedt32.exe). Теперь добавьте нашего пользователя (какие вы ему права дадите - не важно). После этого закройте окно свойств и удалите этого пользователя. Появится сообщение с кратким рассказом о SID :)) После удаления откройте снова закладку безопасность. Теперь пользователя test там не должно уже быть, но вероятнее всего на его месте будет строка, например такая S-1-5-21-1409072233-1202550629-1957994488-1045 (я проверял это на Win2k Prof, а на NT4 Server пользователь просто "исчезал". Так что, если вы ничего не увидели - поверьте наслово :)) Это и есть тот самый SID (в текстовом виде).
Давайте разберем эти циферки. Текстовый SID имеет вид S-R-I-S-S.... Начинается он с префикса S. Далее идут - R - номер версии (пока это 1),
I (48бит) - уполномоченный орган, выдавший SID (в примере это 5 - создано WinNT),
S (32бита) - (один или несколько) уполномоченный орган нижнего уровня, выдавший SID, называемый RID (relative identifier).
Уполномоченный орган, выдавший SID (I) - 48-разрядное число, объявленное так
type SID_IDENTIFIER_AUTHORITY = record Value : array[0..5] of Byte; end;
Пока уполномоченных органов, выдающих SID пять и значит мы будем проверять значение этого числа по последнему байту. В SID должен быть хот один RID. Ниже - таблица стандартных уполномоченных органов и соответствующих им стандартных RID (в скобках - их числовое значение).
SECURITY_NULL_SID_AUTHORITY (0) - применяется для создания пустой группы
- SECURITY_NULL_RID (0) [Sid пустой группы S-1-0-0]
SECURITY_WORLD_SID_AUTHORITY (1) - применяется для создания учетной записи "Все"
- SECURITY_WORLD_RID (0) [Sid Все(EveryOne) S-1-1-0]
SECURITY_LOCAL_SID_AUTHORITY (2) - применяется для создания группы Local
(в ней находятся все пользователи, регистрирующиеся локально)
- SECURITY_LOCAL_RID (0) [Sid группы Local S-1-2-0]
SECURITY_CREATOR_SID_AUTHORITY (3) - применяется для создания стандартных
SID владельцев
- SECURITY_CREATOR_OWNER_RID (0) - [Создатель/владелец - S-1-3-0]
- SECURITY_CREATOR_GROUP_RID (1) - [Группа владельца - S-1-3-1]
Все вышеописанные SID являются одинаковыми для любой NT. Единственным органом, выдающим RID пользователям и группам, является
SECURITY_NT_AUTHORITY (5). У него также есть несколько стандартных RID. Полный их список вы можете найти в MSDN. Я назову
только один - SECURITY_LOCAL_SYSTEM_RID (18) и SID S-1-5-18 соответствуют учетной записи LocalSystem\Система. В SID, который я дал для примера, то что подчеркнутым - это SID компьютера. А жирным выделен RID пользователя или группы. Пользовательские RID начинаются с 1000. Это значит, что SID в примере был создан 45ым по порядку.
Думаю, теперь стало понятнее, что такое SID. Ну теперь перейдем к практике.
Самое частое действие - получение SID по имени пользователя или группы или наоборот. Делается это функциями LookupAccountName и LookupAccountSid. Окончания в этих функциях (Name и Sid) показывают не то, что надо получить, а то, что у нас есть!!! Т.е. LookupAccountName по имени пользователя получает SID, а LookupAccountSid по SID имя пользователя (или группы..).
// Ищем SID пользователя lpName
procedure UserGetSid(lpName : PWideChar);
var
lpDomain : PWideChar;
Sid : PSID;
cbDomain,cbSid : Cardinal;
peUse : Cardinal;
begin
cbSid:=128; // Размер буфера под SID (должно хватить)
cbDomain:=64; // Кол-во символов в имени домена
// Выделяем память
GetMem(Sid,cbSid);
GetMem(lpDomain,cbDomain*SizeOf(WideChar)); // Я в примерах использую Unicode-строки
// Пытаемся получить SID и имя домена
if not LookupAccountNameW(nil,lpName,Sid,cbSid,lpDomain,cbDomain,peUse)
and (GetLastError=ERROR_INSUFFICIENT_BUFFER)
then begin
// Не хватило памяти под SID и/или имя домена.
// в cbSid и cbDomain находятся необходимые размеры
ReAllocMem(Sid,cbSid);
// Повторюсь - функция требует и возвращает не размер буфера, а кол-во символов.
ReAllocMem(lpDomain,cbDomain*SizeOf(WideChar));
// Снова вызываем
if not LookupAccountNameW(nil,lpName,Sid,cbSid,lpDomain,cbDomain,peUse)
then MessageBoxW(0,PWideChar(SysErrorMessage(GetLastError)),'Error',MB_OK);
end;
// Пользуемся SID
// Освобождаем память
FreeMem(Sid);
Freemem(lpDomain);
end;
// Получаем имя пользователя по SID
//(комментировать не буду - все аналогично предыдущему примеру)
procedure GetSidUser(Sid : PSID);
var
Sid : PSID;
lpName,lpDomain : PWideChar;
cbName,cbDomain : Cardinal;
peUse : Cardinal;
begin
cbName:=64;
cbDomain:=64;
GetMem(lpName,cbName*SizeOf(WideChar));
GetMem(lpDomain,cbDomain*SizeOf(WideChar));
if not LookupAccountSidW(nil,Sid,lpName,cbName,lpDomain,cbDomain,peUse)
and (GetLastError=122) then begin
ReAllocMem(lpName,cbName*SizeOf(WideChar));
ReAllocMem(lpDomain,cbDomain*SizeOf(WideChar));
if not LookupAccountSidW(nil,Sid,lpName,cbName,lpDomain,cbDomain,peUse)
then MessageBoxW(0,PWideChar(SysErrorMessage(GetLastError)),'Error',MB_OK);
end;
// Пользуемся lpName
ShowMessage(lpName);
FreeMem(lpName);
Freemem(lpDomain);
end;
Наличие имени сервера (локального компьютера (nil) в данном случае) не означает, что поиск будет вестись только на этом
компьютере. Функция просматривает объекты в таком порядке - стандартные SID, пользователи и группы на этом компьютере, домен
и все компьютеры в нем, другие домены. При этом, если имя учетной записи совпадает с именем компьютера, то она вернет SID
последнего.Последним параметром у этих функций переменная типа SID_NAME_USE = DWORD. В нее после выполнения функции помещается тип SID.
const SidTypeUser = 1; // SID пользователя SidTypeGroup = 2; // группы SidTypeDomain = 3; // домена SidTypeAlias = 4; // встроенной группы SidTypeWellKnownGroup = 5; // стандартной группы SidTypeDeletedAccount = 6; // удаленной записи SidTypeInvalid = 7; // неверный SID SidTypeUnknown = 8; // неизвестный SID SidTypeComputer = 9; // SID компьютера (объявления в windows.pas нет.)
Еще одна операция - создание стандартных SID. Делается это функцией AllocateAndInitializeSid (в принципе этой функцией можно создать (почти) любой SID). Для примера создадим SID стандартной группы СЕТЬ (Network).
Текстовый вид этого SID S-1-5-2.
const SECURITY_NT_AUTHORITY : TSidIdentifierAuthority = (Value: (0, 0, 0, 0, 0, 5)); // создаем SID AllocateAndInitializeSid(SECURITY_NT_AUTHORITY,1,2,0,0,0,0,0,0,0,Sid); // используем SID // Освобождаем память FreeSid(Sid);
Итак, еще раз S-1-5-2. 1 - версия; орган, выдавший SID 5 = SECURITY_NT_AUTHORITY; 2 - RID группы СЕТЬ. Параметры функции : орган, выдавший SID (SECURITY_NT_AUTHORITY), кол-во RID (1), список до 8 RID (используется только 2). В итоге мы получаем в Sid SID группы СЕТЬ. Но мы не выделяли память под этот Sid! За нас это сделала система => она и должна ее освободить. Делается это функцией FreeSid().
ЗЫ. В реальности кол-во RID ограничено 15тью, но в AllocateAndInitializeSid их может быть не больше 8.
Другие функции для SID.
EqualSid(Sid1,Sid2) сравнивает 2 SID.
EqualPrefixSid(Sid1,Sid2); сравнивает 2 SID без последнего RID
GetLengthSid(Sid) возвращает размер Sid в байтах
IsValidSid(Sid); проверяет SID на "правильность"
CopySid(dwSidLength,ToSid,FromSid); копирует FromSid в ToSid,dwSidLength - размер буфера ToSid (GetLengthSid)
И еще две функции только для Win2000 и далее. Переводят SID в строку и наоборот
ConvertSidToStringSid(Sid,StringSid);
ConvertStringSidToSid(StringSid,Sid);
// Их описания нет в Windows.pas, но объявить их можно примерно так
function ConvertSidToStringSidW(Sid : PSID;var StringSid : PWideChar):Boolean;stdcall;external 'advapi32.dll';
function ConvertStringSidToSid(StringSid : PWideChar; var Sid : PSID):Boolean;stdcall;external 'advapi32.dll';
Обе функции требуют освободить память var-параметров функцией LocalFree.
В MSDN сама структура SID описывается как
SID = record Revision : Byte; SubAuthorityCount : Byte; IdentifierAuthority : SID_IDENTIFIER_AUTHORITY; SubAuthority : array [0..ANYSIZE_ARRAY-1] of DWORD; end;
Но не смотря на ее задокументированность, работать с ней напрямую крайне не рекомендуется!!! Для этих целей есть специальные функции, работу с которыми я покажу на примере получения текстового SID.
function SidToStr(Sid : PSID):WideString; var SIA : PSidIdentifierAuthority; dwCount : Cardinal; I : Integer; begin // S-R-I-S-S... Result:=''; // Проверяем SID if not isValidSid(Sid) then Exit; Result:='S-'; // Префикс // Получаем номер версии SID // Хотя работать на прямую с SID, как я уже говорил, не рекомендуется Result:=Result+IntToStr(Byte(Sid^))+'-'; // Получаем орган, выдавший SID // Пока все находится в последнем байте sia:=GetSidIdentifierAuthority(Sid); Result:=Result+IntToStr(sia.Value[5]); //S-R-I- // кол-во RID dwCount:= GetSidSubAuthorityCount(Sid)^; // и теперь перебираем их for i:=0 to dwCount-1 do Result:=Result+'-'+IntToStr(GetSidSubAuthority(Sid,i)^); end;
Вопросы без ответов
Вопрос по структуре USER_INFO_2
Если можно помогите разобраться
в USER_INFO_2 (и аналогичных USER_INFO_3 и не помню далее цифры) есть два поля
last_logon:DWORD;
last_logoff:DWORD;
первое - время входа пользователя в систему прекрасно получается получить хоть и опрашивать все сервера для получения наибольшего правильного времени а вот второе last_logoff всегда 0 (время выхода не известно) и тоже опрос по всем серверам домена а очень хотелось бы получить это самое время выхода пользователя из системы что для этого сделать с серваками или может есть еще другой путь (анализировать журнал событий ну как то грустно хотелось бы напрямую из AD)
Если кто может помочь с ответом, напишите пожалуйста на flint@vtc.ru
Вот и все на сегодня.
С уважением, ведущий рассылки FliNT
delphi.xonix.ru
| http://subscribe.ru/
E-mail: ask@subscribe.ru |
Отписаться
Убрать рекламу |
| В избранное | ||
