• Атрибуты безопасности
  • Общий обзор средств безопасности: дескриптор безопасности
  • Списки контроля доступа
  • Использование объектов безопасности Windows
  • Права объектов и доступ к объектам
  • Инициализация дескриптора безопасности
  • Управляющие флаги дескриптора безопасности
  • Идентификаторы безопасности
  • Работа с ACL
  • Пример: использование разрешений на доступ в стиле UNIX к файлам NTFS
  • Пример: инициализация атрибутов защиты
  • Комментарии к программе 15.3
  • Чтение и изменение дескрипторов безопасности
  • Пример: чтение разрешений на доступ к файлу
  • Пример: изменение разрешений на доступ к файлу
  • Защита объектов ядра и коммуникаций
  • Защита именованных каналов
  • Защита объектов ядра и приватных объектов
  • Значения маски АСЕ
  • Пример: защита процесса и его потоков
  • Обзор дополнительных возможностей защиты объектов
  • Удаление элементов АСЕ
  • Абсолютные и самоопределяющиеся относительные дескрипторы безопасности
  • Системные списки ACL
  • Информация, хранящаяся в маркерах доступа
  • Управление идентификаторами SID
  • Протокол защищенных сокетов
  • Резюме
  • В следующей главе
  • Дополнительная литература
  • Упражнения
  • ГЛАВА 15

    Безопасность объектов Windows

    Windows поддерживает тщательно продуманную модель безопасности, которая исключает возможность несанкционированного доступа к таким объектам, как файлы, процессы или отображения файлов. Защитить можно почти любой из совместно используемых (разделяемых) объектов, и программист располагает возможностями управления правами доступа с высокой степенью их детализации.

    Windows как единая система зарегистрирована в Оранжевой книге Управления национальной безопасности США (National Security Agency Orange Book) как система с сертифицированным уровнем безопасности С2, который требует обеспечения разграничительного контроля доступа с возможностью разрешения или запрещения тех или иных прав доступа к объекту на основании идентификационных данных пользователя, пытающегося получить доступ к объекту. Кроме того, система безопасности Windows распространяется на сетевую среду.

    Тема безопасности слишком обширна, чтобы ее можно было полностью рассмотреть в рамках одной главы. Поэтому внимание в данной главе сосредоточено непосредственно на демонстрации того, каким образом API безопасности Windows используется для защиты объектов от несанкционированного доступа. Хотя средства контроля доступа образуют лишь подмножество функциональных средств безопасности Windows, они представляют самый непосредственный интерес для тех, кто хочет ввести элементы защиты в программы, приведенные в данной книге. Самый первый пример, программа 15.1, демонстрирует эмуляцию системы полномочий (permissions) на доступ к файлам, принятой в UNIX, в случае файлов NTFS, тогда как во втором примере в роли защищаемых объектов выступают именованные каналы. Те же принципы далее могут быть использованы для организации защиты других объектов. В списке литературы указаны источники, обратившись к которым вы сможете получить дополнительную информацию по обеспечению безопасности объектов.

    Описанные средства защиты будут работать только под управлением Windows NT, и их нельзя использовать в системах семейства Windows 9x.

    Атрибуты безопасности

    В этой главе мы исследуем средства контроля доступа Windows сверху вниз, чтобы увидеть, как строится система безопасности объектов. Вслед за общим обзором, но перед тем, как мы приступим к примерам, будут подробно описаны соответствующие функции Windows. В случае файлов для проверки и изменения некоторых атрибутов безопасности объектов NTFS можно воспользоваться проводником (Windows Explorer).

    Почти для всех объектов, создаваемых при помощи системного вызова Create, предусмотрен параметр атрибутов безопасности (security attributes). Следовательно, программы могут защищать файлы, процессы, потоки, события, семафоры, именованные каналы и так далее. Первым шагом является включение указателя на структуру SECURITY_ATTRIBUTES в вызов Create. До сих пор мы всегда указывали в своих программах значение NULL для этого указателя или же использовали структуру SECURITY_ATTRIBUTES просто для создания наследуемых дескрипторов (глава 6). В реализации защиты объекта важную роль играет элемент lpSecurityDescriptor структуры SECURITY_ATTRIBUTES, являющийся указателем на дескриптор безопасности (security descriptor), который содержит описание владельца объекта и определяет, каким пользователям предоставлены те или иные права доступа или в каких правах им отказано.

    Структура SECURITY_ATTRIBUTES была введена в главе 6, но для удобства мы еще раз приведем ее полное определение.

    typedef struct _SECURITY_ATTRIBUTES {

     DWORD nLength;

     LPVOID lpSecurityDescriptor;

     BOOL bInheritHandle;

    } SECURITY ATTRIBUTES;

    Значение параметра nLength следует устанавливать равным:

    sizeof(SECURITY_ATTRIBUTES)

    Параметр bInheritHandle управляет свойствами наследования дескриптора объекта другими процессами.

    Отдельные компоненты дескриптора безопасности описываются в следующем разделе.

    Общий обзор средств безопасности: дескриптор безопасности

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

    Дескриптор безопасности инициализируется функцией InitializeSecurityDescriptor и состоит из следующих элементов:

    • Идентификационный номер владельца (Security Identifier, SID) (описывается в следующем разделе, в котором рассматривается все, что связано с владельцами объектов).

    • SID группы.

    • Список разграничительного контроля доступа (Discretionary Access Control List, DACL) — список элементов, в явной форме регламентирующих права доступа к объекту для определенных пользователей или групп. В нашем обсуждении термин "ACL", употребляемый без префикса "D", будет относиться к DACL.

    • Системный ACL (System ACL, SACL), иногда называемый ACL аудиторского доступа (audit access ACL).

    Функции SetSecurityDescriptorOwner и SetSecurityDescriptorGroup связывают идентификаторы SID с дескрипторами безопасности, о чем говорится далее в разделе "Идентификаторы безопасности".

    ACL инициализируются функцией Initialize ACL, а затем связываются с дескриптором безопасности с помощью функций SetSecurityDescriptorDacl и SetSecurityDescriptorSacl.

    Атрибуты безопасности подразделяются на абсолютные (absolute) и самоопределяющиеся относительные (self-relative). На данном этапе мы не будем делать различия между ними, но вернемся к этому вопросу далее в настоящей главе. Дескриптор безопасности и его компоненты представлены на рис. 15.1.

    Списки контроля доступа

    Каждый ACL состоит из совокупности элементов контроля доступа (Access Control Entry, АСЕ). Существует два типа АСЕ: для разрешения данного вида доступа (allowed) и его запрета (denied).

    Сначала список ACL инициализируют посредством функции InitializeAcl, a затем добавляют в него элементы АСЕ. Каждый АСЕ содержит SID и маску доступа (access mask), определяющую, какие именно права доступа предоставляются пользователю или группе, идентифицируемым по их SID, а в каких им отказано. В качестве типичного примера прав доступа к файлам можно привести права доступа FILE_GENERIC_READ и DELETE.

    Добавление элементов АСЕ в разграничительные списки ACL осуществляется при помощи двух функций — AddAccessAllowedAce и AddAccessDenieddAce. Функция AddAuditAccessAce служит для добавления элементов в SACL, что позволяет отслеживать попытки доступа, осуществляемые с использованием указанного SID. 

    Рис. 15.1. Строение дескриптора безопасности


    Наконец, для удаления АСЕ из списка используется функция DeleteAce, а для извлечения — функция GetAce.

    Использование объектов безопасности Windows

    В дескриптор безопасности вносятся многочисленные подробные данные, и на рис. 15.1 отражены лишь основные элементы его структуры. Заметьте, что у каждого процесса также имеется свой SID (содержащийся в маркере доступа), который используется ядром для того, чтобы определить, какие виды доступа разрешены или какие виды доступа подлежат аудиту. Кроме того, маркер доступа (access token) может предоставлять владельцу определенные привилегии (privileges) (свойственная данному владельцу способность выполнять операции, перекрывающая права (rights), указанные в списке ACL). Так, администратор может иметь привилегии на выполнение операций чтения и записи ко всем файлам, не имея на это прав, явно заданных в списке ACL данного файла.

    Если пользовательские или групповые идентификаторы доступа не обеспечивают, ядро просматривает права доступа, указанные в ACL. Определяющую роль играет первый встреченный элемент, дающий возможность воспользоваться данной запрошенной услугой или отказывающий в этом. Поэтому очередность, в которой в список вносятся элементы АСЕ, имеет большое значение. Во многих случаях АСЕ, запрещающие доступ, располагаются первыми, чтобы конкретный пользователь, которому необходимо запретить данный вид доступа, не мог получить его, воспользовавшись членством в группе, которой этот вид доступа предоставлен. В то же время, для получения желаемой семантики в программе 15.1 существенно, чтобы элементы АСЕ, предоставляющие и запрещающие доступ, могли располагаться в произвольном порядке. АСЕ, отказывающий во всех видах доступа, может располагаться последним для гарантии того, что доступ не будет разрешен никому, если только он конкретно не указан в АСЕ.

    Права объектов и доступ к объектам

    Любой объект, например файл, получает свои права доступа при создании, однако впоследствии эти права могут быть изменены. Процессу требуется доступ к объекту, когда он запрашивает дескриптор, используя для этого, например, вызов функции CreateFile. В одном из параметров запроса дескриптора содержится указание на желаемый вид доступа, например FILE_GENERIC_READ. Если у процесса имеются необходимые права на получение требуемого доступа, запрос завершается успешно. Для различных дескрипторов одного и того же объекта может быть определен различный доступ. Для указания флагов доступа используются те же значения, которые использовались для предоставления прав или отказа в них при создании ACL. 

    Стандартом UNIX (без С2 или других расширений) поддерживается более простая модель безопасности. Эта модель ограничивается файлами и основана на предоставлении полномочий доступа к файлам. В рассматриваемых в настоящей главе примерах программ эмулируется система полномочий доступа UNIX.

    Инициализация дескриптора безопасности

    Сначала необходимо инициализировать дескриптор безопасности с помощью функции InitializeSecurityDescriptor. Параметр pSecurityDescriptor должен указывать адрес действительной структуры SECURITY_DESCRIPTOR. Эти структуры являются непрозрачными для пользователя и управляются специальными функциями.

    Для параметра dwRevision следует устанавливать значение:

    SECURITY_DESCRIPTOR_REVISION
     

    BOOL InitializeSecurityDescriptor(PSECURITY_DESCRIPTOR pSecurityDescriptor, DWORD dwRevision)
     

    Управляющие флаги дескриптора безопасности

    Флаги, входящие в структуру Control дескриптора безопасности, а именно, флаги SECURITY_DESCRIPTOR_CONTROL, определяют, какой смысл приписывается дескриптору безопасности. Некоторые из них устанавливаются и сбрасываются при помощи функций, которые будут рассмотрены далее. Доступ к управляющим флагам обеспечивают функции GetSecurityDescriptorControl и GetSecurityDescriptorControl (доступны в версии NT5), однако эти флаги не будут непосредственно использоваться в наших примерах.

    Идентификаторы безопасности

    Для идентификации пользователей и групп Windows использует идентификаторы SID. Программа может отыскивать SID по учетному имени (account name), которое может относиться к пользователю, группе, домену и так далее. Учетное имя может относиться и к удаленной системе. Сначала мы рассмотрим определение SID по учетному имени. 

    BOOL LookupAccountName(LPCTSTR lpSystemName, LPCTSTR lpAccountName, PSID Sid, LPDWORD cbSid, LPTSTR ReferencedDomainName, LPDWORD cbReferencedDomainName, PSID_NAME_USE peUse)
     

    Параметры

    lpSystemName и lpAccountName — указывают на системное и учетное имена. Для параметра lpSystemName часто используется значение NULL, обозначающее локальную систему.

    Sid — возвращаемая информация, хранящаяся в структуре размером *cbSid. Если размер буфера недостаточно велик, функция выполняется с ошибкой, возвращая размер, который требуется.

    ReferencedDomainName — строка, состоящая из *cbReferencedDomainName символов. Параметр длины строки должен инициализироваться размером буфера (для обработки ошибок используются обычные методы). Возвращаемое значение указывает домен, в котором обнаружено данное имя. В случае учетного имени Administrators возвращается значение BUILTIN, тогда как в случае пользовательского учетного имени возвращается имя этого пользователя. 

    peUse — указывает на переменную SID_NAME_USE (перечислительный тип данных), проверяемыми значениями которой могут быть SidTypeWellKnownGroup, SidTypeUser, SidTypeGroup и так далее.

    Получение имени учетной записи и имени пользователя

    При известном SID можно обратить процесс и получить имя учетной записи, используя функцию LookupAccountSid. Эта функция требует указания SID и возвращает соответствующее имя. Возвращаемым именем может быть любое имя, доступное процессу. Некоторые из имен, например Everyone, известны системе. 

    BOOL LookupAccountSid(LPCTSTR lpSystemName, PSID Sid, LPTSTR lpAccountName, LPDWORD cbName, LPTSTR ReferencedDomainName, LPDWORD cbReferencedDomainName, PSID NAME USE peUse)

    Для получения учетного имени пользователя процесса (пользователя, вошедшего в систему) служит функция GetUserName. 

    BOOL GetUserName(LPTSTR lpBuffer, LPDWORD nSize)
     

    Указатель на строку с именем пользователя и длина этой строки возвращаются обычным образом.

    Для создания SID и управления ими могут использоваться такие функции, как InitializeSid и AllocateAndInitializeSid. Однако в примерах мы ограничимся использованием только SID, получаемых по учетному имени.

    Полученные SID можно вносить в инициализированные дескрипторы безопасности. 

    BOOL SetSecurityDescriptorOwner(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID pOwner, BOOL bOwnerDefaulted)

    BOOL SetSecurityDescriptorGroup(PSECURITY_DESCRIPTOR pSecurityDescriptor, PSID pGroup, BOOL bGroupDefaulted)
     

    pSecurityDescriptor — указатель на соответствующий дескриптор безопасности, a oOwner (или pGroup) — адрес SID владельца (группы). Если для параметра bOwnerDefaulted (или bGroupDefaulted) установлено значение TRUE, то для извлечения информации о владельце (или первичной группе) используется механизм, заданный по умолчанию. В соответствии с этими двумя параметрами устанавливаются флаги SE_OWNER_DEFAULTED и SE_GROUP_DEFAULTED в структуре SECURITY_DESCRIPTOR_CONTROL.

    Аналогичные функции GetSecurityDescriptorOwner и GetSecurityDescriptorGroup возвращают SID (пользователя или группы), извлекая соответствующую информацию из дескриптора безопасности.

    Работа с ACL

    В этом разделе показано, как работать со списками ACL, связывать ACL с дескриптором безопасности и добавлять ACL. Взаимосвязь между этими объектами и соответствующими функциями представлена на рис. 15.1.

    Сначала необходимо инициализировать структуру ACL. Поскольку непосредственный доступ к ACL осуществляться не должен, его внутреннее строение для нас безразлично. Однако программа должна предоставить буфер, выступающий в роли ACL; обработка содержимого выполняется соответствующими функциями. 

    BOOL InitializeAcl(PACL pAcl, DWORD cbAcl, DWORD dwAclRevision)
     

    pAcl — адрес предоставляемого программистом буфера размером cbAcl байт. В ходе последующего обсуждения и в программе 15.4 будет показано, как определить размер ACL, но для большинства целей размера 1 Кбайт будет вполне достаточно. Значение параметра dwAclRevision следует устанавливать равным ACL_REVISION.

    Далее мы должны добавить АСЕ в желаемом порядке, используя функции AddAccessAllowedAce и AddAccessDeniedAce. 

    BOOL AddAccessAllowedAce(PACL pAcl, DWORD dwAclRevision DWORD dwAccessMask, PSID pSid)

    BOOL AddAccessDeniedAce(PACL pAcl, DWORD dwAclRevision, DWORD dwAccessMask, PSID pSid)
     

    Параметр pAcl указывает на ту же структуру ACL, которая была инициализирована функцией InitializeACL, а параметр dwAclRevision также следует устанавливать равным ACL_REVISION. Параметр pSid указывает на SID, например на тот, который был получен с помощью функции LookupAccountName.

    Права, которые предоставляются или в которых отказывается пользователю или группе, идентифицируемым данным SID, определяются маской доступа (dwAccessMask).

    Последнее, что потребуется сделать — это связать ACL с дескриптором безопасности. В случае разграничительного ACL для этого используется функция SetSecurityDescriptorDacl. 

    BOOL SetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, BOOL bDaclPresent, PACL pAcl, BOOL fDaclDefaulted)
     

    Значение параметра bDaclPresent, равное TRUE, указывает на то, что в структуре pAcl имеется ACL. Если этот параметр равен FALSE, то следующие два параметра, pAcl и fDaclDefaulted, игнорируются. Флаг SE_DACL_PRESENT структуры SECURITY_DESCRIPTOR_CONTROL также устанавливается равным значению этого параметра.

    Значение FALSE параметра fDaclDefaulted указывает на то, что ACL был сгенерирован программистом. В противном случае ACL был получен с использованием механизма, принятого по умолчанию, например, путем наследования; вместе с тем, для указания того, что имеется ACL, значение параметра должно быть равным TRUE. Флаг SE_DACL_PRESENT структуры SECURITY_DESCRIPTOR_CONTROL также устанавливается равным значению этого параметра.

    Доступны и другие функции, предназначенные для удаления и считывания АСЕ из ACL; они обсуждаются далее в этой главе. А теперь настало время обратиться к примеру.

    Пример: использование разрешений на доступ в стиле UNIX к файлам NTFS

    Система разрешений на доступ к файлам, принятая в UNIX, предоставляет удобную возможность проиллюстрировать работу системы безопасности Windows, хотя последняя по своему характеру является гораздо более общей, чем стандартные средства защиты UNIX. В приведенной ниже реализации создается девять АСЕ, предоставляющих или запрещающих доступ по чтению, записи или запуску файлов на выполнение владельцу (owner), группе (group) и прочим пользователям (everyone). Предусмотрены две команды. 

    1. chmodW — имитирует UNIX-команду chmod. В данной реализации возможности команды расширены за счет того, что в случае отсутствия указанного файла он будет создан, а также за счет того, что пользователю предоставляется возможность указывать имя группы.

    2. lsFP — расширенный вариант команды lsW (программа 3.2). Если запрошен вывод подробной информации, то отображается имя пользователя-владельца файла, а также результат интерпретации существующих ACL, которые могли быть установлены командой chmodW.

    Указанные две команды представлены программами 15.1 и 15.2. В программах 15.3, 15.4 и 15.5 реализованы три вспомогательные функции.

    1. InitializeUnixSA, которая создает действительную структуру атрибутов безопасности, соответствующих набору разрешений доступа UNIX. Эта функция обладает достаточной общностью, чтобы ее можно было применять по отношению к таким объектам, отличным от файлов, как процессы (глава 6), именованные каналы (глава 11) и объекты синхронизации (глава 8).

    2. ReadFilePermissions.

    3. ChangeFilePermissions.

    Примечание

    Приведенные ниже программы являются упрощенными вариантами программ, представленных на Web-сайте книги. В полных вариантах программ используются отдельные массивы AllowedAceMasks и DeniedAceMasks, в то время как в листингах ниже задействован только один массив.

    Использование отдельного массива DeniedAceMasks обеспечивает невозможность запрета прав доступа SYNCHRONIZE, поскольку флаг SYNCHRONIZE устанавливается во всех трех макросах FILE_GENERIC_READ, FILE_GENERIC_WRITE и FILE_GENERIC_EXECUTE, которые являются комбинациями нескольких флагов (см. заголовочный файл WINNT.H). Дополнительные разъяснения предоставляются в полном варианте программы, доступном на Web-сайте. Кроме того, в полном варианте программы проверяется, не указано ли в командной строке групповое имя; ниже мы будем везде предполагать, что указывается имя пользователя.

    Программа 15.1. chmodW: изменение разрешений на доступ к файлу 

    /* Глава 15. Команда chmodW. */

    /* chmodW [опции] режим файл [ИмяГруппы].

       Изменение режима доступа к именованному файлу.

       Опции:

        -f Принудительный режим — не выводить предупреждающие сообщения в случае невозможности изменения режима.

        –с Создать файл, если он не существует. Необязательное имя группы указывается после имени файла. */

    /* Требуются NTFS и Windows NT (под управлением Windows 9x программа работать не будет). */


    #include "EvryThng.h" 


    int _tmain(int argc, LPTSTR argv[]) {

     HANDLE hFile, hSecHeap;

     BOOL Force, CreateNew, Change, Exists;

     DWORD Mode, DecMode, UsrCnt = ACCT_NAME_SIZE;

     TCHAR UsrNam[ACCT_NAME_SIZE];

     int FileIndex, GrpIndex, ModeIndex;

     /* Массив прав доступа к файлу, следующих в том порядке, который принят в UNIX. */

     /* Эти права будут различными для объектов различного типа. */

     /*ПРИМЕЧАНИЕ: в полном варианте программы, находящемся на Web-сайте, */

     /*используются отдельные массивы масок разрешения и запрещения доступа.*/

     DWORD AceMasks[] = {

      FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE

     };

     LPSECURITY_ATTRIBUTES pSa = NULL;

     ModeIndex = Options(argc, argv, _T("fc"), &Force, &CreateNew, NULL);

     GrpIndex = ModeIndex + 2;

     FileIndex = ModeIndex + 1;

     DecMode = _ttoi(argv[ModeIndex]);

     /* Режим защиты представляет собой восьмеричное число. */

     Mode = ((DecMode / 100) % 10) * 64 /*Преобразовать в десятичное число.*/

          + ((DecMode / 10) % 10) * 8 + (DecMode % 10);

     Exists = (_taccess(argv[FileIndex], 0) == 0);

     if (!Exists && CreateNew) {

      /* Файл не существует; создать новый файл. */

      GetUserName(UsrNam, &UsrCnt);

      pSa = InitializeUnixSA(Mode, UsrNam, argv[GrpIndex], AceMasks, &hSecHeap);

      hFile = CreateFile(argv[FileIndex], 0, 0, pSa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

      CloseHandle(hFile);

      HeapDestroy(hSecHeap); /* Освободить память, занимаемую структурами безопасности. */

     }

     else if (Exists) { /* Файл существует; изменить разрешения доступа. */

      Change = ChangeFilePermissions(Mode, argv[FileIndex], AceMasks);

     }

     return 0;

    }
     

    В программе 15.2 представлена соответствующая часть команды lsFP, а именно, функция Process Item. 

    Программа 15.2. lsFP: перечисление разрешений на доступ к файлу 

    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.

    Вряд ли следует подчеркивать тот факт, что для выполнения этих операций требуются соответствующие полномочия. Так, для успешного выполнения функции SetFileSecurity необходимо либо иметь полномочия на уровне WRITE_DAC, либо быть владельцем объекта.

    Функции GetSecurityDescriptorOwner и GetSecurityDescriptorGroup позволяют извлекать идентификаторы SID из дескриптора безопасности, полученного при помощи функции GetFileSecurity. Для получения ACL следует воспользоваться функцией GetSecurityDescriptorDacl. 

    BOOL GetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR pSecurityDescriptor, LPBOOL lpbDaclPresent, PACL *pAcl, LPBOOL lpbDaclDefaulted)
     

    Параметры этой функции почти полностью совпадают с параметрами функции GetSecurityDescriptorDacl за исключением того, что возвращаются флаги,указывающие на то, действительно ли представлен разграничительный ACL и был ли он установлен по умолчанию или пользователем.

    Чтобы иметь возможность интерпретировать список ACL, необходимо выяснить, сколько элементов АСЕ в нем содержится. 

    BOOL GetAclInformation(PACL pAcl, LPVOID pAclInformation, DWORD cbAclInfo, ACL INFORMATION CLASS dwAclInfoClass) 

    В большинстве случае параметр информационного класса ACL, dwAclInfoClass, равен AclSizeInformation, а параметр pAclInformation представляет собой структуру типа ACL_SIZE_INFORMATION. Другим возможным значением параметра класса является AclRevisionInformation.

    В структуру ACL_SIZE_INFORMATION входят три элемента, наиболее важным из которых является AceCount, который указывает, сколько элементов содержится в списке. Чтобы выяснить, достаточно ли велик размер ACL, проверьте значения элементов AclBytesInUse и AclBytesFree структуры ACL_SIZE_INFORMATION.

    Функция GetAce извлекает извлекает АСЕ по заданному индексу. 

    BOOL GetAce(PACL pAcl, DWORD dwAceIndex, LPVOID *pAce)
     

    Для получения определенного элемента АСЕ (их общее количество теперь известно) следует указать его индекс. рАсе указывает на структуру АСЕ, в которой имеется элемент под названием Header, содержащий, в свою очередь, элемент АсеТуре. Для проверки типа можно использовать значения ACCESS_ALLOWED_ACE и ACCESS DENIED АСЕ. 

    Пример: чтение разрешений на доступ к файлу

    Программа 15.4 представляет собой функцию ReadFilePermissions, которая используется программами 15.1 и 15.2. Эта программа методично использует описанные выше функции для извлечения нужной информации. Правильная работа этой программы зависит от того факта, что ACL были созданы с помощью программы 15.3. Функция включена в тот же исходный модуль, что и программа 15.3, поэтому соответствующие объявления не повторяются.

    Программа 15.4. ReadFilePermissions: чтение атрибутов безопасности 

    DWORD ReadFilePermissions(LPCTSTR lpFileName, LPTSTR UsrNm, LPTSTR GrpNm)

    /* Возвращает разрешения на доступ к файлу в стиле UNIX. */

    {

     PSECURITY_DESCRIPTOR pSD = NULL;

     DWORD LenNeeded, PBits, iAce;

     BOOL DaclF, AclDefF, OwnerDefF, GroupDefF;

     BYTE DAcl[ACL_SIZE];

     PACL pAcl = (PACL)&DAcl;

     ACL_SIZE_INFORMATION ASizeInfo;

     PACCESS_ALLOWED_ACE pAce;

     BYTE AType;

     HANDLE ProcHeap = GetProcessHeap();

     PSID pOwnerSid, pGroupSid;

     TCHAR RefDomain[2][DOM_SIZE];

     DWORD RefDomCnt[] = {DOM_SIZE, DOM_SIZE);

     DWORD AcctSize[] = {ACCT_NAME_SIZE, ACCT_NAME_SIZE};

     SID_NAME_USE sNamUse[] = {SidTypeUser, SidTypeGroup};

     /* Получить требуемый размер дескриптора безопасности. */

     GetFileSecurity(lpFileName, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, pSD, 0, &LenNeeded);

     pSD = HeapAlloc(ProcHeap, HEAP_GENERATE_EXCEPTIONS, LenNeeded);

     GetFileSecurity(lpFileName, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, pSD, LenNeeded, &LenNeeded);

     GetSecurityDescriptorDacl(pSD, &DaclF, &pAcl, &AclDefF);

     GetAclInformation(pAcl, &ASizeInfo, sizeof(ACL_SIZE_INFORMATION), AclSizeInformation);

     PBits = 0; /* Вычислить разрешения на доступ на основе ACL. */

     for (iAce = 0; iAce < ASizeInfo.AceCount; iAce++) {

      GetAce(pAcl, iAce, &pAce);

      AType = pAce->Header.AceType;

      if (AType == ACCESS_ALLOWED_ACE_TYPE) PBits |= (0x1 << (8-iAce));

     } 

     /* Определить имя владельца и владеющей группы. */

     GetSecurityDescriptorOwner(pSD, &pOwnerSid, &OwnerDefF);

     GetSecurityDescriptorGroup(pSD, &pGroupSid, &GroupDefF);

     LookupAccountSid(NULL, pOwnerSid, UsrNm, &AcctSize[0], RefDomain[0], &RefDomCnt[0], &sNamUse[0]);

     LookupAccountSid(NULL, pGroupSid, GrpNm, &AcctSize[1], RefDomain[1], &RefDomCnt[1], &sNamUse[1]);

     return PBits;

    }
     

    Пример: изменение разрешений на доступ к файлу

    Программа 15.5 является последней в нашем собрании функций, предназначенных для работы со средствами защиты файлов. Эта функция, ChangeFilePermissions, заменяет существующий дескриптор безопасности новым, сохраняя идентификаторы SID пользователя и группы, но создавая новый разграничительный список ACL.

    Программа 15.5. ChangeFilePermissions: изменение атрибутов безопасности 

    BOOL ChangeFilePermissions(DWORD fPm, LPCTSTR FNm, LPDWORD AceMsk)

    /* Изменить разрешения на доступ к существующему файлу. Разрешения на доступ для группы остаются неизменными. */

    {

     TCHAR UsrNm[ACCT_NAME_SIZE], GrpNm[ACCT_NAME_SIZE];

     LPSECURITY_ATTRIBUTES pSA;

     PSECURITY_DESCRIPTOR pSD = NULL;

     HANDLE hSecHeap;

     if (_taccess(FNm, 0) != 0) return FALSE;

     ReadFilePermissions(FNm, UsrNm, GrpNm);

     pSA = InitializeUnixSA(fPm, UsrNm, GrpNm, AceMsk, &hSecHeap);

     pSD = pSA->lpSecurityDescriptor;

     SetFileSecurity(FileName, DACL_SECURITY_INFORMATION, pSD);

     HeapDestroy(hSecHeap);

     return TRUE;

    }
     

    Комментарии по поводу разрешений на доступ к файлам

    В процессе выполнения этих программ весьма интересно контролировать файловую систему через проводник Windows. Эта служебная программа не в состоянии интерпретировать АСЕ, разрешающие и запрещающие доступ, и не может отображать соответствующие разрешения. В Windows 4.0 проводник, сталкиваясь с такими АСЕ, будет генерировать исключения.

    Вместе с тем, использование ACL, разрешающих и запрещающих доступ, необходимо для эмуляции семантики UNIX. Если этим пренебречь, то проводник Windows сможет обеспечить просмотр разрешений. Тогда, например, при коде защиты 0446 пользователь и члены группы смогут осуществлять запись в файл, поскольку это разрешено всем пользователям категории Everyone. В то же время, UNIX действует иначе; пользователю и членам группы эта операция будет запрещена.

    Понаблюдайте также за тем, что происходит, когда вы пытаетесь создать защищенный файл на дискете или в другой FAT-системе, а также когда программа выполняется под управлением Windows 9x.

    Защита объектов ядра и коммуникаций

    В предыдущих разделах мы говорили главным образом о защите файлов, хотя те же методы можно применять и к другим объектам, построенным по типу файлов, например, именованным каналам (глава 11) или объектам ядра. Наш следующий пример, программа 15.6, предназначен для работы с именованными каналами, с которыми можно обращаться во многом точно так же, как с файлами.

    Защита именованных каналов

    Хотя соответствующая часть кода в листинге программы 11.3 опущена, сервер, полный программный код которого находится на Web-сайте книги, предоставляет возможность защиты его именованных каналов для предотвращения доступа к ним пользователей, не обладающих необходимыми полномочиями. Необязательные параметры командной строки позволяют указать имя пользователя и групповое имя:

    Server [ИмяПользователя ИмяГруппы]

    Если имена пользователя и группы опущены, используются коды защиты, заданные по умолчанию. Заметьте, что для создания необязательных атрибутов защиты в полной версии программы 11.3 (которая доступна на Web-сайте) и в программе 15.6 используются методы из программы 15.3. В то же время, вместо вызова функции InitUnixSA мы теперь вызываем более простую функцию InitializeAccessOnlySA, которая обеспечивает предоставление только доступа, разрешенного элементами АСЕ, и помещает последний АСЕ, запрещающий доступ, в конец списка ACL. В программе 15.6 представлены соответствующие участки кода, которые не были отражены в листинге программы 11.3. В случае именованных каналов важное значение имеют следующие права доступа:

    • FILE_GENERIC_READ

    • FILE_GENERIC_WRITE

    • SYNCHRONIZE (разрешает потоку ожидать освобождения канала)

    Если при подключении клиента требуется предоставить все права доступа, можно просто указать уровень доступа STANDARD_RIGHTS_REQUIRED. Для получения полного доступа (дуплексного, входящего, исходящего и так далее) вам также придется воспользоваться маской 0x1FF. В сервере, представленном в программе 15.6, предусмотрена защита экземпляров его именованных каналов с использованием этих прав доступа. Доступ к каналу имеют только клиенты, запущенные на выполнение владельцем канала, хотя предоставление доступа к каналу также членам группы не вызывает никаких сложностей.

    Программа 15.6. ServerNP: защита именованного канала 

    /* Глава 15. ServerNP. Предусмотрена защита именованного канала.

     * Многопоточный сервер командной строки. Версия на основе

     * именованного канала.

     * Использование: Server [ИмяПользователя ИмяГруппы]. */

    _tmain(int argc, LPTSTR argv[]) {

     …

     HANDLE hNp, hMonitor, hSrvrThread[MAXCLIENTS];

     DWORD iNp, MonitorId, ThreadId;

     DWORD AceMasks[] = /* Права доступа к именованному каналу. */

      {STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0X1FF, 0, 0 };

     LPSECURITY_ATTRIBUTES pNPSA = NULL;

     …

     if (argc == 4) /* Необязательный параметр защиты канала. */

      pNPSA = InitializeAccessOnlySA(0440, argv[1], argv[2], AceMasks, &hSecHeap);

     …

     /* Создать экземпляр канала для каждого серверного потока. */

     …

     for (iNp = 0; iNp < MAXCLIENTS; iNp++) {

      hNp = CreateNamedPipe(SERVER_PIPE, PIPE_ACCESS_DUPLEX, PIPE_READMODE_MESSAGE | PIPE_TYPE_MESSAGE | PIPE_WAIT, MAXCLIENTS, 0, 0, INFINITE, pNPSA);

      if (hNp == INVALID_HANDLE_VALUE) ReportError(_T("Невозможно открыть именованный канал."), 1, TRUE);

     }

     …

    }
     

    Защита объектов ядра и приватных объектов

    Многие объекты, такие как процессы, потоки или мьютексы, являются объектами ядра (kernel objects). Для получения и установки дескрипторов безопасности ядра используются функции GetKernelObjectsSecurity и SetKernelObjectsSecurity, аналогичные функциям защиты файлов, описанным в настоящей главе. Однако при этом вы должны знать, какие права доступа соответствуют данному объекту; в следующем разделе показано, как определить эти права. 

    Существует также возможность связывания дескрипторов безопасности с приватными, сгенерированными программой объектами, такими как объекты Windows Sockets или патентованные базы данных. Соответствующими функциями являются GetPrivateObjectSecurity и SetPrivateObjectSecurity. Ответственность за принудительное введение определенных прав доступа к таким объектам несет программист, который для изменения дескрипторов безопасности должен использовать функции CreatePrivateObjectSecurity и DestroyPrivateObjectSecurity.

    Значения маски АСЕ

    Модели "пользователь, группа, прочие", которую реализует функция InitUnixSA в большинстве случаев будет вполне достаточно, хотя с использованием тех же базовых методов могут реализовываться и другие модели.

    Вместе с тем, для этого необходимо знать фактические значения маски АСЕ, которые соответствуют тому или иному объекту ядра. Эти значения не всегда достаточно хорошо документированы, но для их нахождения можно воспользоваться несколькими способами.

    • Прочитайте документацию с описанием функции открытия интересующего вас объекта. Флаги доступа имеют те же значения, что и флаги, используемые в маске АСЕ. Так, функция OpenMutex использует флаги MUTEX_ALL_ACCESS и SYNCHRONIZE (второй из указанных флагов требуется для любого объекта, который может использоваться с функциями WaitForSingleObject или WaitForMultipleObjects). Другие объекты, например процессы, имеют множество других дополнительных флагов доступа.

    • Полезная в этом отношении информация может содержаться также в документации по функциям "создания" объектов.

    • Проверьте, не содержатся ли флаги, применимые к интересующему вас объекту, в заголовочных файлах WINNT.H и WINBASE.Н.

    Пример: защита процесса и его потоков

    В документации по функции OpenProcess представлена подробная градация прав доступа, соответствующих самым разнообразным функциям, выполнение которых требует применения дескриптора процесса.

    Так, значение PROCESS_TERMINATE параметра доступа разрешает процессу (а фактически — потока внутри процесса) использовать дескриптор процесса в функции TerminateProcess для завершения процесса.

    Доступ на уровне PROCESS_QUERY_INFORMATION разрешает использование дескриптора процесса при вызове функций GetExitCodeProcess или GetPriorityClass, тогда как уровень доступа PROCESS_ALL_ACCESS разрешает любой доступ, а доступ SYNCHRONIZE требуется для выполнения функций ожидания завершения процесса. 

    Чтобы проиллюстрировать эти идеи, на основе программы JobShell, рассмотренной в главе 6, была разработана программа JobShellSecure.c, которая разрешает доступ к управляемому процессу только его владельцу (или администратору). Эта программа находится на Web-сайте книги.

    Обзор дополнительных возможностей защиты объектов

    О средствах безопасности Windows можно было сказать намного больше, но настоящая глава является лишь введением в эту тему, показывая, как организовать защиту объектов Windows, используя API системы безопасности. В последующих разделах кратко рассмотрены дополнительные вопросы, относящиеся к этой тематике.

    Удаление элементов АСЕ

    Функция DeleteAce удаляет АСЕ, определяемый с помощью индекса аналогично тому, как это делается в случае функции GetAce.

    Абсолютные и самоопределяющиеся относительные дескрипторы безопасности

    Программа 15.5, позволяющая изменять ACL, удобна тем, что просто заменяет один дескриптор безопасности (SD) другим. В то же время, при замене существующих SD следует проявлять осторожность, поскольку они бывают двух типов: абсолютные (absolute) и самоопределяющиеся относительные (self-relative). Внутреннее устройство этих структур данных для наших целей не имеет значения, однако вы должны понимать, в чем состоит различие между ними, и как переходить от одного из них к другому.

    • В процессе создания SD они являются абсолютными, и входящие в них указатели указывают на различные структуры, находящиеся в памяти. По сути, функция InitializeSecurityDescriptor создает абсолютный SD.

    • При связывании SD с постоянно существующим объектом, например файлом, ОС объединяет все данные, относящиеся к SD, в одну компактную самоопределяющуюся структуру. В то же время, изменение SD (например, изменение ACL) порождает трудности при управлении пространством в пределах структуры абсолютного SD.

    • Имеется возможность преобразовывать SD из одной формы в другую при помощи соответствующих функций Windows. Чтобы преобразовать самоопределяющийся относительный SD, например, возвращенный функцией GetFileSecurity, в абсолютный, используйте функцию MakeAbsoluteSD. Для обратного преобразования SD после внесения необходимых изменений служит функция MakeSelfRelativeSD. Функция MakeAbsoluteSD относится к числу тех функций Windows, которым огромное количество параметров придает устрашающий вид: из одиннадцати ее параметров по два приходится на каждый из четырех компонентов SD, по одному — на входной и выходной SD, а последний параметр предназначен для хранения размера результирующего абсолютного SD.

    Системные списки ACL

    Для управления системными списками ACL предусмотрен полный набор функций, однако использовать их может только системный администратор. Системные ACL определяют, какие разрешения на доступ к объекту должны быть зарегистрированы. Основной является функция AddAuditAccessAce, аналогичная функции AddAccessAllowed. В случае системных списков ACL понятие запрещенного доступа отсутствует.

    Двумя другими функциями, предназначенными для работы с системными ACL, являются функции GetSecurityDescrtiptorSacl и SetSecurityDescrtiptorSacl. Эти функции сопоставимы с их аналогами, предназначенными для работы с разграничительными ACL, — GetSecurityDescrtiptorDacl и SetSecurityDescrtiptorDacl.

    Информация, хранящаяся в маркерах доступа

    Программа 15.1 не решает задачи получения имен групп, связанных с процессом в его маркере доступа (access token). В ней просто требуется, чтобы имя группы указывал пользователь. Для получения соответствующей информации предназначена функция GetTokenInformation, требующая использования дескриптора процесса (глава 6). Эта задача решается в упражнении 15.12, в котором содержится подсказка к правильному решению. Сам код решения можно найти на Web-сайте книги.

    Кроме того, в маркере доступа хранится информация о привилегиях доступа, так что процесс получает определенный доступ в соответствии со своими идентификационными данными, а не в соответствии с полномочиями доступа, связанными с объектом. Так, администратору требуется доступ, перекрывающий тот, который предоставляется данным конкретным объектом. Здесь опять необходимо обратить ваше внимание на различие между правами доступа (rights) и привилегиями (privileges).

    Управление идентификаторами SID

    В наших примерах SID получались по именам пользователя и группы, но вы также можете создавать новые SID с помощью функции AllocateAndlnitializeSid. Дополнительно имеется возможность получать информацию о SID при помощи других функций, а также копировать SID (CopySid) и сравнивать их между собой (CompareSid).

    Протокол защищенных сокетов

    Интерфейс Windows Sockets (Winsock), описанный в главе 12, обеспечивает связь между системами по сети. Winsock удовлетворяет промышленным стандартам, что делает возможным взаимодействие с системами, не принадлежащими семейству Windows. Протокол защищенных сокетов (Secure Sockets Layer, SSL), являющийся расширением Winsock, располагает уровень протокола безопасной передачи данных поверх базового транспортного протокола, что обеспечивает возможность аутентификации, шифрования и дешифрации сообщений.

    Резюме

    В Windows реализована тщательно разработанная модель безопасности объектов, возможности которой значительно превышают возможности стандартной системы защиты файлов UNIX. В примерах программ было показано, как эмулировать принятую в UNIX систему разрешений доступа и прав владения, устанавливаемых с помощью функций umask, chmod и chown. Программы также могут устанавливать владельца (группу и пользователя). Описанная эмуляция не является простой, однако результирующие функциональные возможности оказываются гораздо шире стандартных возможностей UNIX. Эта сложность обусловлена требованиями стандарта С2, изложенными в Оранжевой книге (Orange Book C2), в которых для определения списков управления доступом и и владельцами объектов используются маркеры доступа.

    В следующей главе

    Эта глава завершает наше рассмотрение Windows API. Следующая глава содержит обсуждение Win64, являющегося 64-битовым расширением Win32 API, и демонстрирует, как добиться того, чтобы программы правильно компоновались и выполнялись как в 32-битовом, так и в 64-битовом режимах.

    Дополнительная литература

    Windows

    Администрирование систем безопасности и политики безопасности Windows обсуждаются в [2]. Углубленному рассмотрению проблем безопасности посвящена книга [32]. 

    Строение и архитектура Windows NT

    Подробное описание внутренней реализации механизмов безопасности Windows содержится в [38].

    Стандарт безопасности Orange Book C2

    Протокол безопасной передачи данных по сети С2 и другие уровни безопасности определены в публикации Министерства обороны США DoD Trusted Computer System Evaluation Criteria.

    Упражнения

    15.1. Расширьте возможности программы 15.1 таким образом, чтобы несколько групп могли иметь собственные уникальные разрешения доступа. Пары "имя-разрешение" могут выступать в качестве отдельных аргументов функции.

    15.2. Расширьте возможности программы 15.4 таким образом, чтобы она могла выводить список всех групп, в дескрипторах безопасности объектов которых имеются АСЕ.

    15.3. Убедитесь в том, что программа chmodW обеспечивает желаемое ограничение доступа к файлу.

    15.4. Исследуйте, какие атрибуты безопасности по умолчанию вы получаете вместе с файлом.

    15.5. Назовите другие маски доступа, которые можно использовать вместе с АСЕ. Дополнительную информацию вы можете найти в документации Microsoft.

    15.6. Усовершенствуйте программы chmodW и lsFP таким образом, чтобы при попытке обработки файла, не относящегося к системе NTFS, выводилось сообщение об ошибке. Для этого потребуется использовать функцию GetVolumeInformation.

    15.7. Усовершенствуйте программу chmodW, предусмотрев для нее опцию –о, позволяющую указывать, что пользователем программы является пользователь-владелец.

    15.8. Определите фактический размер буфера ACL, необходимый программе 15.3 для хранения элементов АСЕ. В программе 15.3 для этой цели используется 1024 байт. Можете ли вы предложить формулу для расчета необходимого размера буфера ACL?

    15.9. На Web-сайте Cygwin (http://www.cygwin.com) предлагается великолепная Linux-среда с открытым исходным кодом для Windows, предоставляющая командный процессор и реализацию таких команд, как chmod и ls. Установите эту среду и сравните варианты команд, реализованные в этой среде, с теми, которые разработаны в данной книге. Например, будет ли программа lsFP правильно отображать разрешения на доступ к файлу, если они были установлены с помощью соответствующей команды Cygwin, и наоборот. Сравните исходный код, представленный на Web-сайте Cygwin, с примерами из данной главы, что позволит вам критически оценить оба подхода к использованию средств безопасности Windows.

    15.10. В библиотеку совместимости входят функции _open и _unmask, которые позволяют обрабатывать разрешения на доступ к файлам. Исследуйте, каким образом они эмулируют систему разрешений на доступ к файлам, принятую в UNIX, и сравните их с решениями, приведенными в этой главе.

    15.11. Напишите программу для команды whoami, отображающей имя пользователя, который вошел в систему.

    15.12. В программе 15.3, в которой создается дескриптор безопасности, требуется, чтобы программист предоставил имя группы. Модифицируйте программу таким образом, чтобы она создавала разрешения для всех пользовательских групп. Подсказка. Необходимо воспользоваться функцией OpenTokenProcess, возвращающей массив с именами групп, хотя вам потребуется провести некоторые эксперименты для выяснения способа хранения имен групп в массиве. Частичное решение вы найдете в исходном тексте программы, находящемся на Web-сайте.

    15.13. Обратите внимание на то, что в клиент-серверной системе клиенты имеют доступ строго к тем же файлам и другим объектам, которые доступны серверу, установленному на серверной машине с правами доступа сервера. Снимите это ограничение, реализовав так называемое делегирование прав доступа (security delegation), используя функции ImpersonateNamedPipeClient и RevertToSelf. Клиенты, не принадлежащие группе, которая применялась для защиты канала, подключаться к серверу не смогут.

    15.14. Существует ряд других функций Windows, которые вы можете счесть полезными для себя и применить для упрощения или усовершенствования программ, предложенных в качестве примеров. Ознакомьтесь со следующими функциями: AreAllAccessesGranted, AreAnyAccessesGranted, AccessCheck и MapGenericMask. Можете ли вы воспользоваться этими функциями для упрощения или усовершенствования примеров? 







     


    Главная | В избранное | Наш E-MAIL | Добавить материал | Нашёл ошибку | Наверх