Джонсон Харт - Системное программирование в среде Windows
static BOOL ProcessItem(LPWIN32_FIND_DATA pFileData, DWORD NumFlags, LPBOOL Flags)
/* Вывести список атрибутов с указанием разрешений доступа и владельца. */
/* Требуются NTFS и Windows NT (под управлением Windows 9x программа работать не будет). */
{
DWORD FType = FileType(pFileData), Mode, i;
BOOL Long = Flags[1];
TCHAR GrpNam[ACCT_NAME_SIZE], UsrNam[ACCT_NAME_SIZE];
SYSTEMTIME LastWrite;
TCHAR PermString[] = _T("---------");
const TCHAR RWX[] = {'r','w','x'}, FileTypeChar[] = {' ', 'd'};
if (FType != TYPE_FILE && FType != TYPE_DIR) return FALSE;
_tprintf(_T("n"));
if (Long) {
Mode = ReadFilePermissions(pFileData->cFileName, UsrNam, GrpNam);
if (Mode == 0xFFFFFFFF) Mode = 0;
for (i = 0; i < 9; i++) {
if (Mode >> (8 – i) & 0x1) PermString[i] = RWX[i % 3];
}
_tprintf(_T("%c%s 18.7s %8.7s%10d"), FileTypeChar[FType – 1], PermString, UsrNam, GrpNam, pFileData->nFileSizeLow);
FileTimeToSystemTime(&(pFileData->ftLastWriteTime), &LastWrite);
_tprintf(_T(" %02d/%02d/%04d %02d:%02d:%02d"), LastWrite.wMonth, LastWrite.wDay, LastWrite.wYear, LastWrite.wHour, LastWrite.wMinute, LastWrite.wSecond);
}
_tprintf(_T(" %s"), pFileData->cFileName);
return TRUE;
}
Далее мы рассмотрим реализацию вспомогательных функций.
Пример: инициализация атрибутов защиты
Программа 15.3 представляет вспомогательную функцию InitializeUnixSA. Эта функция создает структуру атрибутов безопасности, которая содержит ACL с элементами АСЕ, эмулирующими разрешения на доступ к файлам в UNIX. Существует девять АСЕ, предоставляющих или запрещающих доступ по чтению, записи или запуску файлов на выполнение владельцу (owner), группе (group) и прочим пользователям (everyone). Эта структура не является локальной переменной функции и должна распределяться и инициализироваться, а затем возвращаться вызывающей программе; обратите внимание на массив AceMasks в программе 15.1.
Программа 15.3. InitUnFp: инициализация атрибутов защиты/* Задание режима доступа в стиле UNIX посредством элементов АСЕ, хранящихся в структуре SECURITY_ATTRIBUTES. */
#include "EvryThng.h"
#define ACL_SIZE 1024
#define INIT_EXCEPTION 0x3
#define CHANGE_EXCEPTION 0x4
#define SID_SIZE LUSIZE
#define DOM_SIZE LUSIZE
LPSECURITY_ATTRIBUTES InitializeUnixSA(DWORD UnixPerms, LPCTSTR UsrNam, LPCTSTR GrpNam, LPDWORD AceMasks, LPHANDLE pHeap) {
HANDLE SAHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0, 0);
LPSECURITY_ATTRIBUTES pSA = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
PACL pAcl = NULL;
BOOL Success;
DWORD iBit, iSid, UsrCnt = ACCT_NAME_SIZE;
/* Таблицы имен пользователя (User), группы (Group) и прочих пользователей (Everyone), идентификаторов SID и так далее для LookupAccountName и создания SID. */
LPCTSTR pGrpNms[3] = {EMPTY, EMPTY, _T("Everyone")};
PSID pSidTable[3] = {NULL, NULL, NULL};
SID_NAME_USE sNamUse[3] = {SidTypeUser, SidTypeGroup, SidTypeWellKnownGroup};
TCHAR RefDomain[3][DOM_SIZE];
DWORD RefDomCnt[3] = {DOM_SIZE, DOM_SIZE, DOM_SIZE};
DWORD SidCnt[3] = {SID_SIZE, SID_SIZE, SID_SIZE};
__try { /* Блок try-except для исключений при распределении памяти. */
*рНеар = SAHeap;
pSA = HeapAlloc(SAHeap, 0, sizeof (SECURITY_ATTRIBUTES));
pSA->nLength = sizeof(SECURITY_ATTRIBUTES);
pSA->bInheritHandle = FALSE;
/* Программист может выполнить эти установки позже. */
pSD = HeapAlloc(SAHeap, 0, sizeof(SECURITY_DESCRIPTOR));
pSA->lpSecurityDescriptor = pSD;
InitializeSecurityDescriptor(pSD, SECURITY DESCRIPTOR REVISION);
/* Получить SID пользователя, группы и прочих пользователей.
* Другие важные подробности можно найти на Web-сайте. */
pGrpNms[0] = UsrNam;
pGrpNms[1] = GrpNam;
for (iSid = 0; iSid < 3; iSid++) {
pSidTable[iSid] = HeapAlloc(SAHeap, 0, SID_SIZE);
LookupAccountName(NULL, pGrpNms[iSid], pSidTable[iSid], &SidCnt[iSid], RefDomain[iSid], &RefDomCnt[iSid], &sNamUse[iSid]);
}
SetSecurityDescriptorOwner(pSD, pSidTable[0], FALSE);
SetSecurityDescriptorGroup(pSD, pSidTable[1], FALSE);
pAcl = HeapAlloc(ProcHeap, HEAP_GENERATE_EXCEPTIONS, ACL_SIZE);
InitializeAcl(pAcl, ACL_SIZE, ACL_REVISION);
/* Добавить все элементы АСЕ, разрешающие и запрещающие доступ. */
for (iBit = 0; iBit < 9; iBit++) {
if ((UnixPerms >> (8 – iBit) & 0x1) != 0 && AceMasks[iBit%3] != 0) AddAccessAllowedAce(pAcl, ACL_REVISION, AceMasks [iBit%3], pSidTable [iBit/3]);
else if (AceMasks[iBit%3] != 0) AddAccessDeniedAce(pAcl, ACL_REVISION, AceMasks [iBit%3], pSidTable [iBit/3]);
}
/* Добавить запрет доступа для всех АСЕ категории "Прочие". */
Success = Success && AddAccessDeniedAce(pAcl, ACL_REVISION, STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL, pSidTable[2]);
/* Связать ACL с атрибутом защиты. */
SetSecurityDescriptorDacl(pSD, TRUE, pAcl, FALSE);
return pSA;
} /* Конец блока try-except. */
__except(EXCEPTION_EXECUTE_HANDLER) { /* Освободить все ресурсы. */
if (SAHeap != NULL) HeapDestroy(SAHeap);
pSA = NULL;
}
return pSA;
}
Комментарии к программе 15.3
Хотя структура программы 15.3 и может показаться несложной, выполняемую ею операцию вряд ли можно назвать простой. Кроме того, программа иллюстрирует целый ряд моментов, заслуживающих внимания, которые касаются использования средств безопасности Windows.
• Необходимо распределить в памяти несколько областей, предназначенных для хранения нужной информации, например, идентификаторов SID. Эти области создаются в специально выделенной для этих целей куче, которая по завершении работы должна быть уничтожена вызывающей программой.
• В данном примере структура атрибутов безопасности относится к файлам, но она также может использоваться с другими объектами, например именованными каналами (глава 11). В программе 15.4 показано, как встроить такую структуру при работе с файлами.
• Для эмуляции поведения UNIX существенное значение имеет порядок следования элементов АСЕ. Обратите внимание на то, что АСЕ, разрешающие и запрещающие доступ, добавляются в ACL по мере обработки битов, кодирующих полномочия, в направлении слева (Owner/Read) направо (Everyone/Execute). Благодаря этому биты полномочий, заданные, например, кодом защиты 460 (в восьмеричном представлении), будут запрещать пользователю доступ по записи даже в том случае, если он входит в состав группы.
• Права доступа описываются в АСЕ такими значениями, как FILE_GENERIC_READ или FILE_GENERIC_WRITE, которые аналогичны флагам, используемым в функции CreateFile, хотя добавляются и другие флаги доступа, например SYNCHRONIZE. Эти права указываются в вызывающей программе (в данном случае в программе 15.1), чтобы обеспечить их соответствие объекту.
• Значение, определенное для константы ACL_SIZE, выбрано достаточно большим, чтобы выделенных для него разрядов хватило для хранения девяти элементов АСЕ. После того как мы рассмотрим программу 15.5, способ определения требуемого размера элемента данных станет для вас очевидным.
• В функции используются три SID, по одному для каждой из следующих категорий пользователей: User (Пользователь), Group (Группа) и Everyone (Прочие). Для получения имени, используемого в качестве аргумента при вызове функции LookupAccountName, используются три различные методики. Имя обычного пользователя поступает из функции GetUserName. Именем пользователя, относящегося к категории прочих пользователей, является Everyone в SidTypeWellknownGroup. Групповое имя должно предоставляться в виде аргумента командной строки и отыскиваться как SidTypeGroup. Для нахождения групп, которым принадлежит пользователь, требуются определенные сведения о дескрипторах процесса, и решить эту задачу вам предлагается в упражнении 15.12.
• В версии программы, находящейся на Web-сайте книги, в отличие от той, которая представлена здесь, большое внимание уделено проверке ошибок. В ней даже предусмотрена проверка действительности сгенерированных структур с помощью функций IsValidSecurityDescriptor, IsValidSid и IsValidAcl, названия которых говорят сами за себя. Указанное тестирование ошибок оказалось чрезвычайно полезным на стадии отладки.
Чтение и изменение дескрипторов безопасности
После того как дескриптор безопасности связан с файлом, следующим шагом является определение кода защиты существующего файла и его возможное изменение. Для получения и установления кода защиты файла в терминах дескрипторов безопасности используются следующие функции:
BOOL GetFileSecurity(LPCTSTR lpFileName, SECURITY_INFORMATION secInfo, PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD cbSd, LPDWORD lpcbLengthNeeded)
BOOL SetFileSecurity(LPCTSTR lpFileName, SECURITY_INFORMATION secInfo, PSECURITY_DESCRIPTOR pSecurityDescriptor)
Переменная secInfo имеет тип перечисления и принимает значения:
OWNER_SECURITY_INFORMATION
GROUP_SECURITY_INFORMATION
DACL_SECURITY_INFORMATION
SACL_SECURITY_INFORMATION
которые позволяют указать, какую часть дескриптора безопасности необходимо получить или установить. Эти значения могут объединяться при помощи поразрядной операции "или".
Наилучшим способом определения необходимого размера возвращаемого буфера для функции GetFileSecurity является двукратный вызов этой функции. Во время первого вызова значение параметра cbSd может быть задано равным 0. После того как буфер выделен, вызовите эту функцию второй раз. Этот принцип применяется в программе 15.4.