Захист об’єктів в NT, Книги та статті, Різне, статті

Автор: Юрій Спектор, Королівство Delphi



  1. Користувачі та групи
  2. Маркер доступу
  3. Захист об’єктів, дескриптори безпеки
  4. Списки контролю доступу (ACL). DACL
  5. Робота з ACE
  6. Системний список управління доступом (SACL). Аудит
  7. Зміна дескриптора безпеки існуючого об’єкта. Абсолютні і отностітельно дескриптори безпеки
  8. Висновок

Системи лінійки 9x не є розрахованими на багато користувачів в тому розумінні, що не дозволяють розмежувати доступ до ресурсів, а лише дозволяють вибрати профіль – спосіб відображення даних відповідно до настройками того чи іншого користувача. В системах лінійки NT доступ до об’єктів управляється операційною системою. Захищеними об’єктами можуть бути файли, пристрої, поштові ящики, канали, завдання, процеси, потоки, об’єкти синхронізації, порти завершення введення-виведення, розділи загальної пам’яті, мережеві ресурси, розділи реєстру та ін Механізми, про які піде мова далі, застосовні лише до систем лінійки NT.


У даній статті описані в основному тільки механізми роботи. Незважаючи на те, що в статті присутні приклади, що пояснюють ті чи інші моменти, які використовуються в прикладах функції детально не описуються. Функцій, так чи інакше стосуються безпеки, дуже багато. Привести повний їх перелік і опис в рамках даної статті неможливо. Передбачається, що читач вміє користуватися довідковою системою і при необхідності зможе ознайомитися з ними самостійно.


Користувачі та групи


Як вже було сказано, Windows NT є ​​багатокористувацької системою і дозволяє керувати доступом до своїх об’єктів між кількома споживачами. Для кожного зареєстрованого користувача система створює обліковий запис – запис, що містить відомості про даний користувача. Облікові записи всіх користувачів зберігаються в якійсь системної базі даних. Вона являє собою таблицю, схематично показану на малюнку 1.



Рисунок 4. Запит доступу до об’єкта.

Таким чином, продовжуючи аналогію, дескриптор безпеки – це охоронець, маркер доступу – це пропуск, а DACL – це наказ охоронцеві, кому давати доступ, а кому – відмовляти.


Ну і на закінчення глави відзначимо, що ядро ​​звертається до об’єктів не через дескриптори, а через покажчики. Права доступу в режимі ядра не перевіряються, іншими словами, система повністю сама себе довіряє і завжди має доступ до об’єкта.


Робота з ACE


Розглянемо таку ситуацію. А що, якщо два ACE суперечать один одному? Наприклад, один ACE дає повний доступ членам певної групи, а інший – забороняє доступ певному користувачеві з цієї групи. Чи отримає цей користувач доступ до об’єкта? А це залежить від того, в якому порядку ACE розташовані.


Коли процес запитує певний вид доступу до захищеного дескриптором безпеки об’єкту, система діє за наступним алгоритмом:



Щоб було зрозуміліше, пояснимо на прикладі. Припустимо, процес, запущений від імені користувача User1, що входить до групи Group1, запитує доступ до об’єкта на читання і запис. DACL дескриптора безпеки об’єкту має наступний вигляд:




















Рахунок


Вид доступу


Дозволити, заборонити


User1


читання


дозволити


Group1


запис


дозволити


User1


запис


заборонити


Доступ до об’єкта з таким DACL на читання і запис буде дозволений, незважаючи на останній елемент. Так як прохід по DACL починається зверху вниз, а перший елемент надасть право на читання, а другий – на запис. На цьому система і зупиниться. А якщо останній елемент помістити наверх, то доступ буде заборонений.


А тепер поставимо зворотну задачу: в системі зареєстровано 5 користувачів: User1 .. User5. User1 і User2 входять до групи Group1. User3, User4 і User5 входять до групи Group2. Необхідно скласти такий DACL, який:


DACL буде виглядати, наприклад, так:




















Рахунок


Вид доступу


Дозволити, заборонити


User5


повний доступ


заборонити


Group2


читання


дозволити


Group1


повний доступ


дозволити


Тепер на невеликому прикладі продемонструємо роботу з DACL і його елементами. Створимо невеликий додаток, яке після натискання на одну кнопку запускає процес (Windows-калькулятор) і призначає йому дескриптор захисту, що дозволяє всім користувачам будь-який вид доступу, крім PROCESS_TERMINATE (доступ на завершення процесу). При натисканні на іншу кнопку, спробуємо отримати дескриптор цього процесу, зажадавши заборонений вид доступу. Якщо все зробити правильно, на екрані з’явиться віконце, що повідомляє про відмову в доступі. Відразу скажу, що для простоти в даному прикладі опущена всіляка обробка помилок, проте в “бойових” додатках не варто її ігнорувати.

/ / Ця функція буде працювати тільки на системах, починаючи з WinXP
function CreateWellKnownSid(WellKnownSidType: Cardinal; DomainSid, Sid: PSID;
var cbSID: Cardinal): Bool; stdcall; external advapi32 name “CreateWellKnownSid”;
const
WinWorldSid = 1;
aclSize = 1024; ACL_REVISION = 2; / / З невідомих причин, її немає в Windows.pas (D7)
var
pi: TProcessInformation;
procedure TForm1.Button1Click(Sender: TObject);
var
si: TStartupInfo;
sa: TSecurityAttributes;
sd: TSecurityDescriptor;
dacl: PACL;
SID: PSID;
SIDLength: Cardinal;
begin / / Ініціалізували дескриптор безпеки
InitializeSecurityDescriptor(@sd,SECURITY_DESCRIPTOR_REVISION);
GetMem(dacl,aclSize);
try / / Ініціалізували DACL. Виділяємо буфер завідомо більшого розміру / / В принципі, можна точно розрахувати необхідний розмір буфера. / / Як це зробити написано в MSDN в описі функції InitializeAcl
InitializeAcl(dacl^,aclSize,ACL_REVISION);
SIDLength:=0; / / Отримуємо SID групи, що включає всіх користувачів
CreateWellKnownSid(WinWorldSid,nil,nil,SIDLength);
GetMem(SID,SIDLength);
try
CreateWellKnownSid(WinWorldSid,nil,SID,SIDLength); / / Додаємо в DACL ACE, що дозволяє будь-який вид доступу, крім / / PROCESS_TERMINATE всім користувачам. Забороняє ACE додається / / За допомогою функції AddAccessDeniedAce
AddAccessAllowedAce(dacl^,ACL_REVISION,PROCESS_ALL_ACCESS and
not PROCESS_TERMINATE,SID); / / Встановлюємо DACL в дескриптор безпеки
SetSecurityDescriptorDacl(@sd,true,dacl,false); / / Ініціалізували атрибути безпеки і створюємо процес
sa.nLength:=SizeOf(TSecurityAttributes);
sa.lpSecurityDescriptor:=@sd;
sa.bInheritHandle:=false;
FillChar(si,SizeOf(TStartupInfo),0);
si.cb:=SizeOf(TStartupInfo);
CreateProcess(“C:WINDOWSsystem32calc.exe”,nil,@sa,nil,false,0,
nil,nil,si,pi);
CloseHandle(pi.hThread);
finally
FreeMem(SID);
end;
finally
FreeMem(dacl);
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
hProcess: Cardinal;
begin
hProcess:=OpenProcess(PROCESS_TERMINATE,false,pi.dwProcessId);
if hProcess = 0 then
RaiseLastOSError
else
TerminateProcess(hProcess,0);
end;

У прикладі використовується функція CreateWellKnownSid. За допомогою неї ми отримуємо покажчик на SID групи “everyone” (SID цієї групи завжди один і той же – S-1-1-0), яка включає всіх користувачів. Ця функція в даному випадку мені здалася найбільш простий і зручною, проте на системах молодше XP працювати не буде. Ви можете використовувати інший спосіб, наприклад, за допомогою AllocateAndInitializeSid.


Крім функції для роботи з ACL і ACE, що використовуються в прикладі, рекомендую самостійно ознайомитися з AddAccessDeniedAce, AddAce, DeleteAce, GetAce, GetAclInformation, IsValidAcl, SetAclInformation, SetEntriesInAcl.


Системний список управління доступом (SACL). Аудит


Дескриптор безпеки містить покажчик на System Access-Control List (SACL) – список, схожий на DACL, але відповідає не за дозвіл чи заборону на доступ, а за аудит (протоколювання в журналі безпеки) успішних і безуспішних спроб доступу до об’єкта. Завдяки системі аудиту, адміністратор може дізнатися, хто, яким чином і коли користувався (або намагався користуватися, але отримав відмову в доступі) цікавлять його ресурсами. Для роботи з SACL використовуються як загальні для всіх ACL функції, так і специфічні – AddAuditAccessAce, AddAuditDeniedAce, GetSecurityDescriptorSacl, SetSecurityDescriptorSacl.


Зміна дескриптора безпеки існуючого об’єкта. Абсолютні і відносні дескриптори безпеки


У цьому розділі ми повернемося до прикладу з процесом, захищеним від завершення, спробуємо зняти це обмеження і завершити його. А заодно познайомимося з функціями, що дозволяють отримувати та змінювати дескриптор безпеки вже існуючого об’єкта.


Для того щоб змінити DACL об’єкта, процес повинен володіти правом WRITE_DAC, проте, по-перше, ми не забороняли його в DACL (PROCESS_ALL_ACCESS включає це право), а навіть якби заборонили – воно все одно у нас було б на правах власника (поле Owner в дескрипторі безпеки), так як власник завжди має право змінювати DACL.


Для отримання дескриптора безпеки процесу можна скористатися функцією GetKernelObjectSecurity. Але використання цієї функції породжує деякі проблеми. Справа в тому, що при виконанні функції InitializeSecurityDescriptor, виходить так званий абсолютний (absolute) дескриптор безпеки. Свою назву такий тип отримав через те, що покажчики, що містяться в ньому, є абсолютний адресу в пам’яті процесу і вказують на структури пам’яті, що не входять в сам дескриптор. При зв’язуванні дескриптора з об’єктом, система об’єднує всі дані, пов’язані з дескриптору в одну компактну структуру. Такий тип дескриптора називається самовизначається відносний (self-relative). Назва даний тип отримав через те, що покажчики в ньому тепер містять не абсолютний адресу, а відносне зміщення елемента в даній структурі. Такий тип дескриптора зручний, якщо його потрібно скинути в будь сховище, (наприклад – в файл) або переслати по мережі. Також файлова система NTFS зберігає дескриптор безпеки для кожного файлу або папки саме в такому форматі. Перевірити формат дескриптора можна за допомогою GetSecurityDescriptorControl.


Коли ми викликаємо GetKernelObjectSecurity (або інші подібні функції), система повертає саме відносний дескриптор. Багато функцій, такі як SetSecurityDescriptorDacl, вимагають дескриптор безпеки саме в абсолютному форматі. Є спеціальні функції, що дозволяють перетворювати дескриптор з однієї форми в іншу (MakeAbsoluteSD, MakeSelfRelativeSD), однак їх використання робить код дуже громіздким (Функція MakeAbsoluteSD має 11 параметрів!). Інша неприємність полягає в тому, що для роботи з різними типами об’єктів потрібно використовувати різні функції: для об’єктів ядра, таких як процес, потік, мьютекс и др. – Get (Set) KernelObjectSecurity, для файлів – Get (Set) FileSecurity, для user-об’єктів – Get (Set) UserObjectSecurity, для ключів реєстру – RegGet (Set) KeySecurity та ін


На щастя, у цих проблем є одне просте і елегантне рішення – функції GetSecurityInfo і SetSecurityInfo. Їх можна використовувати на системах, починаючи від NT 4.0. Для того щоб продемонструвати переваги цих функцій, напишемо з їх допомогою код, що знімає заборону на завершення процесу. У модулі Windows.pas цих функцій немає (принаймні, на Delphi 7), можете імпортувати їх самостійно або скористатися заголовками JEDI API – http://jedi-apilib.sourceforge.net/.


Отже, кидаємо на форму з прикладу із захищеним процесом ще одну кнопку і прописуємо їй такий обробник клацання:

uses JwaAclApi, JwaAccCtrl; // JEDI
procedure TForm1.Button3Click(Sender: TObject);
var
hProcess: Cardinal;
begin / / Відкриваємо процес. Запитуємо доступ на зміну DACL
hProcess:=OpenProcess(WRITE_DAC,false,pi.dwProcessId);
if hProcess <> 0 then begin / / Встановлюємо покажчик на DACL рівним nil – об’єкт незахищений
SetSecurityInfo(hProcess,SE_KERNEL_OBJECT,DACL_SECURITY_INFORMATION,
nil,nil,nil,nil);
CloseHandle(hProcess);
end
else
RaiseLastOSError;
end;

От і все. У функції SetSecurityInfo ми просто встановлюємо об’єкту nil як DACL. Для порівняння, без використання цієї функції нам потрібно було б:



  1. За допомогою GetKernelObjectSecurity отримати дескриптор безпеки (відносний). Причому функцію довелося б викликати два рази – один для отримання розміру буфера, другий – для отримання самого дескриптора.
  2. Викликати функцію MakeAbsoluteSD для отримання розмірів буферів для DACL, SACL, SID і GROUP.
  3. Виділити пам’ять під ці елементи.
  4. Ще раз викликати MakeAbsoluteSD для перетворення дескриптора в абсолютний формат.
  5. За допомогою SetSecurityDescriptorDacl встановити nil як DACL.
  6. Викликом SetKernelObjectSecurity встановити об’єкту новий дескриптор безпеки.
  7. Звільнити пам’ять, виділену в п.1. і п.2.

Навіть не буду намагатися привести код для порівняння, так як виграш при використанні SetSecurityInfo очевидний. Хоча, саме в даному випадку можна було не отримувати встановлений об’єкту дескриптор і перетворювати його в абсолютний, а створити новий з нуля. Проте в більшості випадків потрібно зберегти багато параметрів існуючого дескриптора, які заздалегідь невідомі.


Тепер запускаємо приклад на виконання. Клацаємо спочатку на першу кнопку – створюємо процес, потім на другу – отримуємо відмову в доступі, на третю – знімаємо з процесу захист, і знову другу – вуаля! Процес завершився.


Хочу також звернути вашу увагу ще на дві функції, подібні SetSecurityInfo і GetSecurityInfo – SetNamedSecurityInfo і GetNamedSecurityInfo. Їх основна відмінність від згаданих раніше в тому, що замість дескриптора об’єкта потрібно вказати його ім’я.


Висновок


В даній статті були розглянуті основні принципи захисту об’єктів на системах лінійки NT. Багато моментів, такі як запуск процесів від імені іншого облікового запису, уособлення або імперсонація (виконання окремих потоків процесу від імені іншого облікового запису), привілеї (особливі повноваження, перелічені в маркері доступу, що мають їх власникам виключні права на доступ до об’єктів, а також дозволяють виконувати привілейовані операції) і багато інших, не були розглянуті, однак сподіваюся, що викладений у статті матеріал допоможе вам самим при необхідності з ними розібратися.


Подяки: SLoW – За поради, конструктивну критику і просто підтримку при написанні цієї статті.

 

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*