Система управления доступом
Система хранит идентификаторы безопасности в бинарной форме, однако существует и текстовая форма представления SID. Текстовая форма используется для вывода текущего значения SID, а также для интерактивного ввода (например, в реестр).
В текстовой форме каждый идентификатор безопасности имеет определенный формат. Вначале находится префикс S, за которым следует группа чисел, разделенных дефисами. Например, SID администратора системы имеет вид: S-1-5-<домен>-500, а SID группы everyone, в которую входят все пользователи, включая анонимных и гостей - S-1-1-0.
Поскольку структура SID имеет переменную длину, для копирования SID и преобразования его в текстовую форму и обратно рекомендуется прибегать к специальным функциям CopySID, ConvertSidToStringSid и ConvertStringSidToSid.
Чтобы получить бинарное значение SID по имени пользователя, нужно использовать функцию LookupAccountName. Обратная задача может быть решена при помощи функции LookupAccountSid.
Прогон программы получения идентификатора безопасности
В качестве иллюстрации рассмотрим несложную программу, задача которой - получение значения Sid для текущей учетной записи и преобразования его в текстовую форму.
#define _WIN32_WINNT 0x0500 #define UNICODE #ifdef UNICODE #define _UNICODE #endif #include <windows.h> #include <stdio.h> #include <sddl.h> void main(void){ wchar_t UserName[256]; int MaxUserNameLength = 256; SID Sid[1024]; PSID pSid; LPTSTR StringSid; DWORD SidSize=1024; SID_NAME_USE SidType; LPTSTR DomainName=NULL; DWORD DomainNameSize=16; // длина имени домена HANDLE hHeap; hHeap = GetProcessHeap(); pSid = &Sid[0]; GetUserName(UserName, &MaxUserNameLength); // получаем имя пользователя DomainName = (LPTSTR)HeapAlloc(hHeap,0,DomainNameSize * sizeof(TCHAR)); LookupAccountName( NULL, // локальный компьютер UserName, pSid, &SidSize, DomainName,&DomainNameSize,&SidType); if (!ConvertSidToStringSid(pSid, &StringSid)) /* память для строки выделяет сама функция */ printf("Convert SID to string SID failed."); wprintf(L"StringSid %s\n", StringSid); LocalFree(StringSid); HeapFree(hHeap,0,DomainName); }
В приведенной программе вначале формируются параметры вызова функции LookupAccountName. В частности, имя текущей учетной записи возвращает функция GetUserName(). Кроме того, необходимо выделить память для имени домена. Результирующее значение SID можно сравнить с тем, которое выдает утилита Whoami.exe.
Написание, компиляция и прогон программы получения имени пользователя по данному значению идентификатора безопасности
На основе предыдущей программы рекомендуется написать программу, которая при помощи функции LookupAccountSid решает обратную задачу - позволяет выяснить имя владельца данного идентификатора безопасности.
Объекты. Дескриптор защиты
В ОС Windows все типы объектов защищены одинаковым образом. С каждым объектом связан дескриптор защиты (security descriptor). Дескриптор защиты описывается структурой типа SECURITY_DESCRIPTOR и инициализируется функцией InitializeSecurityDescriptor.
Связь объекта с дескриптором происходит в момент создания объекта. Например, один из аргументов функции CreateFile - указатель на структуру SECURITY_ATTRIBUTES, которая содержит указатель на дескриптор защиты.
Дескриптор защиты (см. рис. 13.1) содержит SID владельца объекта, SID групп для данного объекта и два указателя на списки DACL (Discretionary ACL) и SACL (System ACL) контроля доступа. DACL и SACL содержат разрешающие и запрещающие доступ списки пользователей и групп, а также списки пользователей, чьи попытки доступа к данному объекту подлежат аудиту.
Структура каждого ACL списка проста. Это набор записей ACE (Access Control Entry), каждая запись содержит SID и перечень прав, предоставленных субъекту с этим SID.
В списке ACL есть записи ACE двух типов - разрешающие и запрещающие доступ. Разрешающая запись содержит SID пользователя или группы и битовый массив (access mask), определяющий набор операций, которые процессы, запускаемые этим пользователем, могут выполнять с данным объектом. Запрещающая запись действует аналогично, но в этом случае процесс не может выполнять перечисленные операции. Битовый массив или маска доступа состоит из 32 битов и обычно формируется программным образом из предопределенных констант, которые описаны в файлах-заголовках компилятора (главным образом в файле WinNT.h). Формат маски доступа можно посмотреть, например, в [ Рихтер ] , [ Рихтер, Кларк ] .
На примере, изображенном на рис. 13.1, владелец файла Александр имеет право на все операции с данным файлом, всем остальным обычно дается только право на чтение, а Павлу запрещены все операции. Таким образом, список DACL описывает все права доступа к объекту. Если этого списка нет, то все пользователи имеют все права; если этот список существует, но он пустой, права имеет только его владелец.
Кроме списка DACL дескриптор защиты включает также список SACL, который имеет такую же структуру, что и DACL, то есть состоит из таких же ACE записей, только вместо операций, регламентирующих доступ к объекту, в нем перечислены операции, подлежащие аудиту. В примере на рис. 13.1 операции с файлом процессов, запускаемых Сергеем, описанные в соответствующем битовом массиве будут регистрироваться в системном журнале.
Задание. Сформировать список прав доступа для файла при помощи инструментальных средств ОС Windows.
Для установки прав доступа к файлу, находящемуся на NTFS разделе диска, нужно выбрать вкладку "Безопасность" апплета "Свойства", который возникает в Windows Explorer при нажатии на ярлык файла правой кнопкой мыши.
Итак, дескриптор защиты имеет достаточно сложную структуру и его формирование выглядит непростой задачей. К счастью, в Windows есть стандартный механизм, назначающий доступ к объектам "по умолчанию", если приложение не позаботилось создать его явно. В таких случаях говорят, что объекту назначена стандартная защита. Примером может служить создание файла при помощи функции CreateFile, где параметру-указателю на структуру SECURITY_ATTRIBUTES присвоено значение NULL. Некоторые объекты используют только стандартную защиту (мьютексы, события, семафоры).
Субъекты хранят информацию о стандартной защите, которая будет назначена создаваемым объектам, в своем маркере доступа (см. следующий раздел). Разумеется, в ОС Windows есть все необходимые средства для настройки стандартной защиты, в частности, списка DACL "по умолчанию", в маркере доступа субъекта.
Основной источник информации о защите объекта - Win32-функция GetSecurityInfo, тогда как настройка защиты объекта может быть осуществлена при помощи функции SetSecurityInfo.
Прогон программы получения информации из дескриптора защиты файла
В качестве примера рассмотрим программу, задача которой - получение текстового значения Sid владельца файла из дескриптора защиты файла.
#define _WIN32_WINNT 0x0500 #ifndef UNICODE #define UNICODE #endif #include <windows.h> #include <stdio.h> #include <sddl.h> #include <Aclapi.h> void main(void){ PSID pSid; PSECURITY_DESCRIPTOR pSD; PACL pDACL; LPTSTR StringSid = NULL; ULONG Error; HANDLE hFile; hFile = CreateFile(TEXT("MyFile.txt"), READ_CONTROL, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); Error = GetSecurityInfo(hFile, SE_FILE_OBJECT, // тип объекта OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, &pSid, // Sid владельца NULL, // Sid группы &pDACL, NULL, // списки прав доступа &pSD // дескриптор защиты); if(Error != ERROR_SUCCESS) printf("GetSecurityInfo Error\n"); if (!ConvertSidToStringSid(pSid, &StringSid)) /* память для строки выделяет сама функция */ printf("Convert SID to string SID failed."); wprintf(L"StringSid %s\n", StringSid); LocalFree(pSD); LocalFree(StringSid); }
Задача данной программы - открыть существующий файл MyFile.txt и применить функцию GetSecurityInfo для извлечения идентификатора безопасности владельца из дескриптора защиты файла. Затем идентификатор преобразуется в текстовую форму и выводится на экран.
Написание, компиляция и прогон программы получения списка контроля доступа к файлу из его дескриптора защиты
На основе предыдущей программы рекомендуется написать программу, которая выводит на экран список прав доступа к обозначенному файлу.
Субъекты безопасности. Процессы, потоки. Маркер доступа
Так же как и объекты, субъекты должны иметь отличительные признаки - контекст пользователя, для того, чтобы система могла контролировать их действия. Сведения о контексте пользователя хранятся в маркере (употребляются также термины "токен", "жетон") доступа. При интерактивном входе в систему пользователь обычно вводит свое имя и пароль. Система (процедура Winlogon) по имени находит соответствующую учетную запись, извлекает из нее необходимую информацию о пользователе, формирует список привилегий, ассоциированных с пользователем и его группами, и все это объединяет в структуру данных, которая называется маркером доступа. Маркер также хранит некоторые параметры сессии, например, время окончания действия маркера. Таким образом, именно маркер является той визитной карточкой, которую субъект должен предъявить, чтобы осуществить доступ к какому-либо объекту.
Вслед за оболочкой (Windows Explorer) все процессы (а также все потоки процесса), запускаемые пользователем, наследуют этот маркер. Когда один процесс создает другой при помощи функции CreateProcess, дочернему процессу передается дубликат маркера, который, таким образом, распространяется по системе.
Основные компоненты маркера доступа показаны на рис. 13.2.
Включая в маркер информацию о защите, в частности, DACL, Windows упрощает создание объектов со стандартными атрибутами защиты. Как уже говорилось, если процесс не позаботится о том, чтобы явным образом указать атрибуты безопасности объекта, на основании списка DACL, присутствующего в маркере, будут сформированы права доступа к объекту по умолчанию. Настройку стандартной защиты можно осуществить при помощи функции SetTokenInformation. При этом, поскольку объекты в Windows отличаются большим разнообразием, в списке DACL "по умолчанию" можно указать только так называемые базовые права доступа, из которых система будет формировать стандартные права доступа в зависимости от вида создаваемого объекта. То, как это делается и как сформировать список DACL, по умолчанию присутствующий в маркере, подробно описано в [ Рихтер ] , [ Рихтер, Кларк ] .
Задание
Осуществить просмотр данных маркера доступа с помощью утилиты WhoAmi
Если процесс обладает правом TOKEN_QUERY доступа к объекту, то большую часть содержимого маркера можно считать при помощи функции GetTokenInformation. Чтобы получить описатель маркера, необходимо воспользоваться функцией OpenProcessToken.
В качестве примера рассмотрим программу, задача которой - получение текстового значения Sid владельца процесса из маркера доступа данного процесса.
#define _WIN32_WINNT 0x0500 #ifndef UNICODE #define UNICODE #endif #include <windows.h> #include <stdio.h> #include <sddl.h> void main(void){ DWORD TokenUserBufSize=256; LPTSTR StringSid; TOKEN_USER *ptUser; HANDLE hHeap; HANDLE hToken = NULL; hHeap = GetProcessHeap(); ptUser = (TOKEN_USER *)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, TokenUserBufSize); if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) printf("OpenProcessToken Error\n"); if (!GetTokenInformation(hToken, // описатель маркера доступа TokenUser, // нужна информация о пользователе ptUser, // буфер для информации TokenUserBufSize, &TokenUserBufSize)) printf("GetTokenInformation Error\n");; if (!ConvertSidToStringSid(ptUser->User.Sid, &StringSid)) printf("Convert SID to string SID failed."); wprintf(L"StringSid %s\n", StringSid); CloseHandle(hToken); LocalFree(StringSid); HeapFree(hHeap,0,ptUser); }
Задача данной программы - получить описатель текущего процесса при помощи функции OpenProcessToken и применить функцию GetTokenInformation для извлечения из него идентификатора безопасности владельца. После чего идентификатор преобразуется в текстовую форму и выводится на экран.
В следующей лекции будет приведен пример программы получения из маркера доступа информации о привилегиях пользователя.