Защита в DCOM/COM+

Защита в Windows

Так как в Windows 9x состояние подсистемы безопасности близко к коматозному, то далее в этом разделе речь пойдет только о Windows NT (и, разумеется, ее потомках – Windows 2000 (W2k), Windows XP и Windows.Net). Если речь все-таки зайдет о Windows 9x, то об этом будет сказано отдельно.

Итак, Windows NT (далее просто NT) – это ОС, поддерживающая защиту класса C2 (начиная с W2k даже чуть больше, за счет возможности шифрования файлов). Это означает, что она обеспечивает защиту в момент, когда компьютер включен и данная ОС загружена. Если компьютер запущен под управлением другой ОС, безопасность не гарантируется.

Как же устроена защита в NT? Без ответа на этот вопрос практически невозможно объяснить, как устроена безопасность в COM. Но полный и развернутый ответ приведет к слишком большому объему информации, который не смогут стерпеть не только страницы этого журнала, но и большинство читателей. :) Поэтому я попробую дать краткое и поверхностное представление о защите в NT. Те, кто уже знаком с этим вопросом, могут или пропустить данный раздел, или просмотреть его мельком, попутно обновив знания и воспроизведя общую картину в своем сознании.

Первое, что нужно понять - NT автоматически обеспечивает защиту только объектов ядра. Остальные объекты могут быть защищены опосредованно через объекты ядра. Например, окно не является объектом ядра и не может иметь самостоятельных атрибутов защиты. Однако, и окна можно защитить, но об этом чуть ниже.

Для начала взгляните на рисунок 1.

Защита в службах Windows и DCOM

Рисунок 1. Схематичное представление защищенных объектов в Windows.

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

Итак перечислим эти сущности:

  • Logon
  • Windows-станция
  • Процесс
  • Поток

Я не упомянул еще одну сущность – Desktop (рабочий стол). Он также может участвовать в системе защиты ОС, но практически не оказывает влияния на работу серверных приложений, так как больше относится к взаимодействию с пользователем.

Кратко пройдемся по перечисленным выше понятиям...

Logon session – я намеренно не переводил это понятие на русский, так как самое близкое по звучанию словосочетание «вход в систему» больше ассоциируется с физическим входом в систему интерактивного пользователя, в то время как logon – чисто логическое понятие, подразумевающее всего лишь процесс аутентификации некоторого пользователя в системе, и не подразумевающее обязательного входа в систему интерактивного (и даже подключенного по сети) пользователя. По сути, logon может произойти и вообще без участия пользователя. Например, logon происходит каждый раз, когда система загружает сервис или COM-сервер, которому указан вариант загрузки, отличный от «Interactive User». В результате этого процесса создается logon-сессия. В терминах систем безопасности участвующая в этом процессе сущность (человек или компьютер) называется принципалом (principal). В системе информация о принципале хранится в его учетной записи. Учетная запись может быть создана не только для принципалов, но и для групп. Однако группа не может быть аутентифицирована, так как она может объединять нескольких принципалов, в том числе опосредованно, через другие группы.

Windows-станция (Windows station или, как ее иногда называют, WinSta) – это логическое пространство, позволяющее защитить окна некоторых процессов от воздействия на них извне. Windows-станции нужны потому, что окна не являются объектами ядра NT, а значит, не могут иметь самостоятельных атрибутов защиты. Любое окно в Windows ассоциировано с некоторым процессом (в котором оно было создано), а процесс, в свою очередь, ассоциирован с некоторой Windows-станцией (реально окна ассоциируются с потоками, а потоки - с десктопами, так что связь окон с оконными станциями опосредованная. Но для простоты восприятия эту особенность можно опустить). В NT только одна Windows-станция может напрямую взаимодействовать с пользователем. Эта станция имеет имя WinSta0. Windows XP и терминальный сервер позволяют создавать несколько интерактивных Windows-станций, каждая из которых, тем не менее, называется WinSta0. Эти станции работают в своих независимых пространствах имен. Неинтерактивные Windows-станции могут создавать окна и манипулировать ими так, как будто бы это нормальные окна, созданные в интерактивной Windows-станции, но эти окна не смогут быть отображены на экране без дополнительных усилий (в принципе существует возможность "на лету" подключить приложение к интерактивной Windows-станции). Как вы, наверное, уже догадались, Windows-станции могут иметь собственные атрибуты защиты. Более того, через них опосредованно и осуществляется защита окон в Windows. Если ваш процесс запущен в Windows-станции A, вы не сможете беспрепятственно послать сообщение окну из Windows-станции B. Честно говоря, в принципе это возможно, но данный процесс связан с большим количеством затруднений и полностью контролируется защитой NT. Кстати сказать, в W2k (без сервис-паков) была дыра в защите Windows, которая позволяла посылать сообщения окнам, находящимся в других Windows-станциях. COM осуществляет синхронизацию межапартаментных вызовов с помощью окон, и, стало быть, Windows-станции имеют непосредственное отношение к защите в COM, так как если бы любой желающий мог посылать сообщения в апартаментные окна, было бы возможно симулировать COM-вызовы.

Процесс – думаю, мало кому нужно объяснять, что такое процесс. Однако краткое определение дать все же стоит. Процесс – это запущенное приложение, которое содержит защищенное (принадлежащее только ему) виртуальное адресное пространство, куда загружаются код и данные. Процессу могут принадлежать некоторые ресурсы операционной системы, такие как открытые файлы, pipe-ы, синхронизирующие объекты (мьютексы, семафоры...), окна и т.п. Процесс также содержит один или более потоков исполнения (нитей), которые могут выполнять код, принадлежащий процессу. Процесс обязательно принадлежит некоторому logоn-у и также может быть ассоциирован с некоторой Windows-станцией.

Поток – логический поток выполнения программы. Два потока могут выполнять код параллельно (с помощью квантования времени или на разных процессорах). Вопреки распространенному заблуждению, код не принадлежит потокам, и функция, выполняемая в одном потоке, не может вызвать функцию в другом. По сути, так даже нельзя говорить (но говорят же!). Однако потоки можно синхронизировать друг с другом. За счет этого можно передавать управление от одного потока другому. Передача управления также возможна через каналы связи (например, pipe-ы или каналы связи COM, в COM под это дело подведена концепция апартаментов). Физически синхронизация осуществляется либо с помощью синхронизирующих примитивов, либо с помощью различного рода очередей, в частности, оконных (с помощью сообщений). При вызове функции передачи сообщения оно ставится в очередь, которая выбирается в потоке, ассоциированном с окном. COM использует эту особенность для организации апартаментов.

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

Как же организован доступ и проверка информации о защите для этих сущностей? Начнем с logon-а и разберем вход интерактивного пользователя в систему. Для начала работы пользователь должен подойти к компьютеру и... очевидно, включить его (по крайней мере один раз это сделать придется точно). :) Через некоторое время перед ним появится диалог входа в систему. Чтобы войти в систему, пользователь должен идентифицировать себя, введя пароль и имя своей (или чужой :) ) учетной записи.

Что же происходит далее?.. А далее происходит создание той самой logon-сессии. Так как logon-сессия – это абстракция, не имеющая вещественного воплощения, для взаимодействия с ней нужно некоторое её физическое представление. Таким представлением является токен. Токен содержит в себе информацию о logon-сессии пользователя, его правах и привилегиях (об этом мы поговорим немного позже).

Далее происходит создание первого процесса. Для интерактивного logon-а таким процессом является оболочка ОС (shell), имя которого записано в ветке реестра HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon. На самом деле, сначала запускается процесс, указанный в значении UserInit (userinit.exe), а уже этот процесс запускает оболочку, прописанную в значении

Shell. Обычно (но не обязательно) такой оболочкой является Windows Explorer. Первому процессу присваивается токен logon-сессии, после чего ему передается управление. С этого момента токен процесса можно узнать с помощью функции OpenProcessToken. Данной функции передается handle процесса (handle текущего можно узнать с помощью GetCurrentProcess) и информация о целях получения данного handle токена (для изменения атрибутов защиты, активации/деактивации разрешенных привилегий, подключения к процессу, дублирования, заимствования прав (имперсонации), запроса информации). Подробное описание данной функции можно найти в MSDN. Скажу только, что для самых любопытных операций всегда нужны привилегии, отсутствующие у обычных пользователей, но имеющиеся у учетной записи SYSTEM, под которой запускаются многие сервисы и сама ОС. Собственно, поэтому ОС и может выполнять столько хитрых операций, не вступая в конфликт с защитой.

Если в текущем процессе создать новый (с помощью функции CreateProcess), то ему будет присвоена копия токена. Хотя атрибуты защиты нового процесса могут и отличаться от атрибутов родительского процесса, токен, определяющий учетную запись, под которой запустится процесс, будет таким же. Такое поведение гарантирует, что новый процесс создается в рамках той же logon-сессии. Таким образом, создается иерархия процессов, находящихся в рамках одного logon-а.

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

Ассоциация потока с токеном, отличным от токена процесса, позволяет потоку прикинуться другим пользователем. Эта технология называется имперсонацией. Имея токен, ассоциированный с другой учетной записью, можно осуществлять операции над защищенными объектами так, как будто работа происходит в процессе, запущенном под другим logon-ом.

Для упрощения имперсонации в Windows имеется функция ImpersonateLoggedOnUser. Для отмены имперсонации можно воспользоваться функцией RevertToSelf. Можно также задавать вручную токен для потока (с помощью функции SetThreadToken).

В Win32 существует функция, позволяющая создать процесс от имени другого пользователя. Эта функция называется CreateProcessAsUser. Именно ею и пользуется система, когда создает первый процесс для интерактивного logon-а, SCM в момент запуска сервиса или COM SCM при запуске COM-сервера. Данная функция среди прочих параметров получает токен, содержащий информацию об учетной записи, под которой будет создан новый процесс. Токен, используемый для создания нового процесса, должен быть так называемым первичным (primary) токеном, т.е. токен, полученный для имперсонации, не может быть использован в данном контексте. Токен можно скопировать с помощью функции DuplicateTokenEx. При этом можно изменить тип токена, но на данную операцию тоже нужно иметь права. В частности, исходный токен должен быть открыт с правом доступа TOKEN_DUPLICATE. Забегая вперед, скажу, что последовательность вызовов DuplicateTokenEx, CreateProcessAsUser позволяет серверному приложению (сервису или COM-серверу) создавать процессы под учетной записью вызывающего клиента.

Для получения токена можно воспользоваться функцией LogonUser. Она создает новую logon-сессию и возвращает токен, содержащий информацию о logon-сессии. Чтобы воспользоваться ей, нужно иметь сведения об учетной записи (имя учетной записи и пароль). Кроме этого, процесс, вызывающий функцию LogonUser, должен обладать привилегией SE_TCB_NAME (известной администраторам как «Act as part of the Operating System»). Это очень мощная привилегия и обычно ее не назначают учетным записям обычных пользователей, но учетной записи сервера она вполне может быть назначена. Учетная запись SYSTEM автоматически обладает данной привилегией. (SYSTEM вообще является самой мощной записью на локальном компьютере, однако она беспомощна, если речь заходит о сети.). В разделе "Вопросы и Ответы" этого номера есть несколько Q&A, описывающих тонкости данного вопроса. Я же позволю себе пропустить эту, безусловно интересную, тему. Замечу только, что использование программного создания logon-сессии с открытым паролем, да и вообще манипуляции с паролем в программе, являются показателем «дурного тона» и неграмотного построения системы защиты. Под NT можно писать большие распределенные приложения, вообще не сталкиваясь с открытыми паролями.

Главное правило в защите под Windows – чем меньше вы ее настраиваете, тем надежнее она будет работать ;). Я, конечно, утрирую, но доля правды в этом высказывании довольно велика.

Дескриптор защиты

Каждый объект ядра обладает дескриптором защиты (Security Descriptor, SD), который, кроме всего прочего, включает в себя список идентификаторов защиты(Security Identifier, SID), представляющих собой идентификаторы учетных записей, которым разрешен или запрещен доступ к объекту.

Этот список называется DACL (читается как дакл или даже джакл). DACL расшифровывается как «Discretionary Access-Control List». По-русски это можно перевести примерно как «список, определяющий контроль над доступом». Каждая запись такого списка содержит информацию о предоставляемых правах, об учетной записи, которой предоставляются данные права (SID) и признак, определяющий, будет ли данная запись «разрешающей» или «запрещающей». Разрешающая запись обычно называемая положительной, а запрещающая – отрицательной.

Идентификаторы защиты, входящие в DACL, могут представлять как конкретного пользователя (или, начиная с W2k, компьютер), так и группу (как домена, так и локальную). Применение групп позволяет уменьшить размер DACL (тем самым ускоряя проверки защиты) и упростить администрирование.

Группа – это сущность, позволяющая объединить несколько учетных записей под одним именем (представляемым SID-ом). Например, если на машине должны работать несколько пользователей, то интерактивный вход можно разрешать не для каждого из них в отдельности, а объединить их в группу и дать такую привилегию только группе. При этом все пользователи получат возможность входить в систему интерактивно.

Пример дескриптора защиты схематично представлен на рисунке 2.

Защита в службах Windows и DCOM

Рисунок 2. Схематичное представление дескриптора защиты.

«SID Владельца» (или Owner SID) определяет, кому принадлежит данный объект. Только один человек может быть владельцем объекта. Учетная запись, под которой создается объект, становится владельцем этого объекта. Владельцу автоматически разрешается изменять права доступа к объекту. Администратор системы (или другая учетная запись, которой это позволено) может взять на себя владение объектом. Но информация о том, что кто-то взял на себя владение объектом, будет доступна, и администратор не сможет вернуть владение учетной записи, владевшей объектом ранее. Администратор не может передать кому-либо владение объектом, он может только присвоить его себе. Таким образом, администратор не сможет поизгаляться над объектом и замести следы, вернув все атрибуты защиты в первоначальное состояние.

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

«SID первичной группы» (или, в оригинале, «Primary Group SID») – это группа, которой разрешен доступ к объекту из POSIX-совместимых приложений. Это поле дескриптора защиты нужно исключительно для совместимости со стандартом POSIX. Ввиду того, что COM-приложения и POSIX имеют мало общего, я не буду рассматривать данный вопрос.

SACL – «System Access-Control List». SACL определяет, будет ли проводиться аудит доступа к объекту. По структуре SACL аналогичен DACL за исключением того, что признак «положительности/отрицательности» определяет не разрешение или запрет доступа, а тип аудита. «+» означает, что аудит будет производиться при успешном доступе к объекту, а «-» – что аудит будет производиться при неудачной попытке доступа к объекту. Таким образом, с помощью SACL можно определять, за кем и в каком случае нужно следить.

Права

Права (access rights/permissions) – это маска доступа к объекту. Разные принципалы в Windows могут иметь разные права. Конкретные права обычно задаются администратором для каждого идентификатора защиты (учетной записи). Настраивать права можно программно или визуально. Например, задать права доступа к файлу или директории можно на странице свойств «Security» (в Explorer или другом файловом менеджере).

Для получения доступа к некоторому объекту его нужно предварительно «открыть». При открытии объекта задаётся тип доступа, который может понадобиться при работе с объектом. При этом происходит сравнение прав, назначенных администратором данному пользователю, с затребованными правами доступа. Если все нормально, ОС возвращает handle на открываемый объект. Данный handle позволяет производить операции, допускаемые затребованными при открытии правами. Если права на некоторую операцию не были затребованы при открытии handle-а, то данная операция запрещается, даже если пользователю разрешен данный вид доступа. Например, некоторому пользователю может быть разрешена запись в некоторый файл, но если файл открыт «на чтение» (с правами, допускающими только чтение), то пользователю не дадут произвести запись в файл через данный handle.

Заметьте, что после открытия некоторого handle-а проверка прав пользователя больше не производится. Этим handle-ом можно пользоваться даже из потока, выполняемого под другой учетной записью, возможно, с меньшими правами. Учитывая это, нужно быть аккуратным при построении политики безопасности серверного приложения. Нелишне напомнить, что сервер, как правило, работает под учетной записью, обладающей большими правами и привилегиями. Политика безопасности должна учитывать, что сервер предоставляет клиенту некий сервис, определяемый интерфейсом сервера. То есть клиент не должен иметь возможности выполнять какие-либо незапланированные действия. Так, с точки зрения безопасности ошибками являются создание универсального интерфейса (позволяющего обойти защиту), запуск клиентских процедур (скриптов), прямое выполнение SQL-запросов, загрузка внешних модулей и т.д.

Привилегии

Привилегии – одна из возможностей в Windows позволить некоторой учетной записи производить разные действия над системой.

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

  1. Привилегии управляют доступом к действиям на системном уровне, а права – доступом к ресурсам.
  2. Привилегии назначаются учетным записям групп и отдельных пользователей, а права доступа – защищаемым объектам на основании записей в DACL объекта.

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

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

Перед выполнением привилегированной операции нужно активизировать нужные привилегии в токене. Для этого нужно вызвать функцию OpenThreadToken или OpenProcessToken, получить от нее handle токена, а затем активизировать нужные привилегии с помощью функции AdjustTokenPrivileges. После выполнения привилегированной операции вызовите AdjustTokenPrivileges снова, чтобы деактивировать привилегии.

В Win32 API определены строковые константы для каждой привилегии.

Администратор может назначать привилегии через User Manager в NT4 и snap-in Security Settings в W2k. Программно их можно назначать с помощью функций LsaAddAccountRights и LsaRemoveAccountRights , а просмотреть их список – с помощью LsaEnumerateAccountRights. Функция LsaEnumerateAccountsWithUserRight перечисляет учетные записи, обладающие указанной привилегией. В таблице 1 приведен список привилегий, поддерживаемых в NT4 и W2k и привилегии, назначенные по умолчанию для учетных записей Administrator (Admin) и System.

* Поддерживается только в Windows 2000

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

Итак, информация о принципале, с которым ассоциирован процесс или поток, хранится в токене, а информация о защите объектов (поддерживающих защиту) находится в дескрипторе безопасности, ассоциированном с этими объектами. Но как же осуществляется проверка прав данного процесса/потока на доступ к некоторому защищенному объекту? Ведь данная информация может быть задана опосредованно через группы! Неужели для каждой проверки приходится обращаться к контролеру домена (Domain Controller – DC)? Это же должно приводить к ужасным задержкам!

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

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

ПРИМЕЧАНИЕ

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

Далее (если совпадений в списке запрещений не обнаружено) аналогичным образом проверяется список разрешений. При этом проверяется, все ли запрошенные права имеются у данного пользователя. Права могут быть заданы раздельно. Например, учетной записи Mike может быть предоставлено только право записи текстового файла А. При этом Mike не сможет открыть этот файл, так как не обладает правом читать файл. Но если Mike входит в группу Users, которой разрешено чтение данного файла, он получит возможность и чтения, и записи – так как права суммируются. Однако если хоть одного запрошенного права нет в списке разрешений, в доступе отказывается, и возвращается ошибка.

Подчеркну еще раз, список запрещений имеет перевес над списком разрешений. Например, одной из часто делаемых ошибок является запрет доступа для группы Everyone (Все) при одновременном разрешении доступа некоторым учетным записям или группам (допустим, членам групп Administrators и Power Users). Делающий это неопытный администратор наивно полагает, что доступ будет дан всем администраторам и продвинутым пользователям с одновременным запретом доступа для всех остальных (нежелательных элементов). Однако в результате, вследствие приоритета запрета над разрешением, никто не сможет получить доступ к объекту с подобными настройками защиты. Правильным выходом в данной ситуации было бы просто убрать запрет для группы Everyone, поскольку никто, кроме администраторов и продвинутых пользователей и так не получит доступа к подопытному объекту.

Для чего же нужны запреты? А для того, чтобы организовать «исключения из правил». Предположим, что нужно дать доступ к объекту всем пользователям, но запретить доступ Васе и Пете. Для этого следует добавить в список разрешений группу Users, а в список запретов Васю и Петю. Вместо прямого добавления Васи и Пети в список запрещений можно создать группу Restricted Users (Пользователи с ограниченными правами), внести Васю и Петю в эту группу и запретить для неё доступ к объекту. Этот подход более гибок, так как защищаемых объектов может быть много. Если впоследствии появятся другие пользователи, которым нужно запретить доступ к этим объектам, не придется изменять права для каждого объекта в отдельности. Можно будет просто добавить необходимых пользователей в группу «Restricted Users».

Кэширование групп хотя и позволяет значительно поднять скорость проверок атрибутов безопасности, но имеет одно неприятное ограничение. Изменение атрибутов защиты учетной записи не отражается на открытых сессиях. Может возникнуть ситуация, когда вы изменили права пользователя (добавив его в группу или, наоборот, удалив его из группы) и считаете, что данный пользователь уже не сможет совершить «незаконных» действий. Но если сессия с данной учетной записью существовала во время изменения прав, данные изменения никак на ней не отразятся. Самое опасное, что администратор может сделать ошибку в настройках защиты, но она не будет заметна до перезапуска системы (так как многие сервисы обычно перезапускаются только вместе с ОС), а перезапуск может произойти со значительной отсрочкой. При этом создается впечатление, будто в ОС вкрался вирус или сверхъестественные силы. Такие ситуации нередко становятся предпосылками для шаманства и необдуманных действий. Как уже говорилось, COM-серверы и их клиенты пользуются теми же механизмами защиты, что и другие части Windows, а значит, имеют те же проблемы и особенности. Следует помнить об этом. Если появилось ощущение, что поведение сервера не соответствует ожидаемому, нужно перезапустить сервер, снова войти в систему (если сервер запускается под интерактивным пользователем), а может быть даже перезапустить систему.

Итак, беглый взгляд на систему безопасности Windows брошен. Перейдем к особенностям защиты в COM.

Защита в COM

Первые версии COM создавались как чисто локальные, и речи о защите как таковой не шло. С появлением DCOM вопрос защиты был включен в повестку дня. К счастью, о защите задумались уже в момент написания спецификации, а не как это зачастую бывает, задним числом.

В чем же заключаются особенности защиты в DCOM по сравнению с обычной защитой в Windows? Главное отличие в том, что COM – объектно-ориентированная модель, в которой не нашлось места для вездесущих handle-ов. Вместо handle-ов в COM используются указатели на интерфейсы, которые являются обыкновенными указателями на область памяти. По сути handle-ы являются некими черными ящиками, в которых можно скрывать разные особенности реализации, в том числе и защиту. Отказ от них привел к невозможности скрыть защиту в недрах ОС. В таких условиях просто невозможно организовать защиту, ведь программист имеет прямой указатель на объект и может делать с этим объектом всё, что угодно. Как же защитить объект? Если объект находится в том же апартаменте, то никак. Но если объект находится в другом апартаменте (для простоты восприятия будем подразумевать в другом процессе, но надо помнить, что это может быть и просто другой поток), то очень даже можно. В этом случае клиент получает указатель не на сам объект, а на proxy. Proxy – это небольшой объект, имеющий те же интерфейсы, что и основной объект, но реализация методов этих интерфейсов занимается всего лишь преобразованием параметров метода в последовательную форму (сериализацией), и пересылкой информации серверной заглушке (stub-у) которая восстанавливает параметры (формируя стековый фрейм) и вызывает настоящий метод объекта. Весь этот процесс в COM называется маршалингом. При возврате управления из метода происходит обратный процесс, вследствие которого клиенту передаются [out]-параметры, возвращаемое значение (HRESULT) и, если произошла ошибка, объект, содержащий описание ошибки.

Защита в службах Windows и DCOM

Рисунок 4. Взаимодействие клиентского процесса и COM-сервера

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

Главное, что нужно понять – по умолчанию метод COM-объекта выполняется под учетной записью серверного процесса. Это очень важный момент! Не осознав его, дальше двигаться нельзя. Представьте себе, в клиентском процессе выполняется некоторый код... этот код выполняется под учетной записью (обычно) имеющей мало прав (по крайней мере на сервере). Код выполняется и в некоторый момент времени доходит до вызова метода удаленного COM-объекта. Управление передается в proxy, та сериализует переданные ей параметры, записывает информацию об учетной записи клиента, устанавливает необходимые параметры защиты и... передает управление DCOM. Тот в свою очередь делает сетевой вызов (с помощью RPC) и передает управление Stub-коду, который восстанавливает полученные данные и вызывает метод COM-объекта. Но (!) код COM-объекта выполняется в контексте защиты сервера, что дает ему (обычно) значительно больше свободы. Сервер выполняет некоторые действия и возвращает управление клиенту. После этого клиент продолжает выполнять свой код в своем контексте защиты. По сути, на время клиент получает права (а вместе с ними и мощь) более привилегированной учетной записи. Но эта мощь ограничена сервисом, предоставляемым сервером. Это очень важный момент, не позволяющий клиенту вмешаться в поведение системы и сделать что-либо нежелательное.

Теперь давайте представим, что может происходить на сервере? Когда вызов приходит на сервер, COM извлекает из полученного буфера информацию о защите и проверяет, можно ли учетной записи, от имени которой происходит вызов, обращаться к серверу. Если в доступе отказано, COM возвращает клиенту E_ACCESSDENIED. Если доступ разрешен, COM вызывает stub для продолжения работы. При этом с потоком, в котором производится вызов, ассоциируется некоторая информация о клиенте. Чуть позже будет рассказано, как получить и использовать эту информацию.

Нетрудно предположить, что информация о клиенте представляется в виде токена (олицетворяющего logon-сессию). Если клиент позволяет серверу использовать этот токен для имперсонации, сервер сможет обращаться к локальным ресурсам компьютера от имени клиента. Подчеркну, именно к локальным ресурсам, например, файлам. Имперсонация не распространяется на вызовы COM-методов или доступ к ресурсам других компьютеров. В W2k появилась новый уровень имперсонации – делегация. Он позволяет серверу вызывать методов удаленных интерфейсов от имени клиента (вызвавшего метод сервера). Подробнее обо всем этом будет рассказано ниже.

Права активации

Под Windows 9x COM-сервер не запускается автоматически по требованию клиента. В ОС этой линейки приходится запускать COM-серверы вручную. Ввиду практически полной незащищенности, плохой устойчивости к сбоям и недостаточной функциональности реализуемых сервисов, Windows 9x вообще лучше не использовать в качестве платформы для серверных приложений. В отличие от Windows 9x, ОС линейки NT прекрасно подходят для запуска COM-серверов. Так, эти ОС поддерживают автоматический запуск приложений удаленными клиентами. Например, если клиент вызвал функцию, активирующую COM-объект (скажем, CoCreateInstance), реализованный в некотором сервере, COM SCM проверяет, зарегистрирована ли для этого объекта в системе фабрика класса. Если не зарегистрирована, то COM SCM находит (через реестр) исполняемый модуль этого сервера, запускает его на выполнение и ждет некоторое время регистрации необходимой фабрики классов.

В ходе этого процесса COM SCM проверяет, имеет ли пользователь, осуществляющий запрос на создание объекта, право на запуск сервера. Сведения о тех, кому разрешено/запрещено запускать сервер, называются «правами активации» (или «launch permissions»). Они хранятся в именованном значении LaunchPermission, находящемся в ветке HKEY_CLASSES_ROOT\AppID\{AppID приложения} реестра. LaunchPermission имеет тип REG_BINARY и хранит сериализованный вид дескриптора безопасности. Настроить права активации можно из утилиты dcomcnfg. Для этого в dcomcnfg.exe нужно найти необходимое приложение, открыть его свойства, перейти на закладку «Security», выбрать пункт «Use custom launch permissions» и нажать на соответствующую (их там много) кнопку «Edit» (см. рисунок 5).

Защита в службах Windows и DCOM

Рисунок 4. Взаимодействие клиентского процесса и COM-сервера

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

Главное, что нужно понять – по умолчанию метод COM-объекта выполняется под учетной записью серверного процесса. Это очень важный момент! Не осознав его, дальше двигаться нельзя. Представьте себе, в клиентском процессе выполняется некоторый код... этот код выполняется под учетной записью (обычно) имеющей мало прав (по крайней мере на сервере). Код выполняется и в некоторый момент времени доходит до вызова метода удаленного COM-объекта. Управление передается в proxy, та сериализует переданные ей параметры, записывает информацию об учетной записи клиента, устанавливает необходимые параметры защиты и... передает управление DCOM. Тот в свою очередь делает сетевой вызов (с помощью RPC) и передает управление Stub-коду, который восстанавливает полученные данные и вызывает метод COM-объекта. Но (!) код COM-объекта выполняется в контексте защиты сервера, что дает ему (обычно) значительно больше свободы. Сервер выполняет некоторые действия и возвращает управление клиенту. После этого клиент продолжает выполнять свой код в своем контексте защиты. По сути, на время клиент получает права (а вместе с ними и мощь) более привилегированной учетной записи. Но эта мощь ограничена сервисом, предоставляемым сервером. Это очень важный момент, не позволяющий клиенту вмешаться в поведение системы и сделать что-либо нежелательное.

Теперь давайте представим, что может происходить на сервере? Когда вызов приходит на сервер, COM извлекает из полученного буфера информацию о защите и проверяет, можно ли учетной записи, от имени которой происходит вызов, обращаться к серверу. Если в доступе отказано, COM возвращает клиенту E_ACCESSDENIED. Если доступ разрешен, COM вызывает stub для продолжения работы. При этом с потоком, в котором производится вызов, ассоциируется некоторая информация о клиенте. Чуть позже будет рассказано, как получить и использовать эту информацию.

Нетрудно предположить, что информация о клиенте представляется в виде токена (олицетворяющего logon-сессию). Если клиент позволяет серверу использовать этот токен для имперсонации, сервер сможет обращаться к локальным ресурсам компьютера от имени клиента. Подчеркну, именно к локальным ресурсам, например, файлам. Имперсонация не распространяется на вызовы COM-методов или доступ к ресурсам других компьютеров. В W2k появилась новый уровень имперсонации – делегация. Он позволяет серверу вызывать методов удаленных интерфейсов от имени клиента (вызвавшего метод сервера). Подробнее обо всем этом будет рассказано ниже.

Категория: Службы и консоли | Добавил: masterov (01.12.2017) | Автор: Андрей Мастеров E W
Просмотров: 165 | Теги: Windows, службы | Рейтинг: 0.0/0
Всего комментариев: 0
avatar