Как работает олицетворение в DCOM?

У меня есть клиентские и серверные приложения DCOM, в которых используется маршаллер OLE. Они отлично работают при работе на одном ПК, но когда сервер находится на другом ПК не в том же домене, я получаю E_ACCESSDENIED (0x80070005).

Сервер ПК настроен с помощью dcomcnfg, чтобы предоставить всем доступ к любому объекту DCOM пользователю, чей логин и пароль я указываю на клиенте. ServerApp и его библиотека типов зарегистрированы на сервере.

Библиотека типов также зарегистрирована на клиентском ПК. Я указываю имя сервера непосредственно в ClientApp, поэтому на ПК клиента не требуется настройка dcomcnfg, насколько я понимаю.

CreateInstanceEx() с именем сервера, логином, доменом и паролем отлично работает. Он возвращает IUnknown и одновременно запускает ServerApp на сервере ПК.

Но когда я пытаюсь выполнить QueryInterface() для интерфейса, который поддерживает сервер, я получаю E_ACCESSDENIED.

Анализ журнала событий безопасности, у меня есть две записи:

Во-первых, успешный сетевой вход пользователя, чьи учетные данные я укажу в ClientApp. Это происходит, когда я вызываю CreateInstanceEx().

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

Теперь, почему черт мог бы этот пользователь войти на сервер, особенно когда я вызываю QueryInterface из всех вещей?

Изучая параметры CreateInterfaceEx, похоже, что происходит какой-то механизм олицетворения. Но неясно, кто олицетворяет кого. Существуют три пользовательских учетных данных:

  • Пользователь, под которым ServerApp запускается на сервере ПК (как настроено в dcomcnfg).

  • Пользователь, чьи учетные данные ClientApp указывает при подключении.

  • Пользователь, чьи учетные данные ClientApp запускается на клиентском ПК.

Независимо от того, как вы смотрите на него, если # 3 задействовано в нем слишком много. Если DCOM собирается идентифицировать/выдавать себя за №3 на сервере ПК, почему мне нужно указать учетные данные # 2? В какой момент?

Казалось бы логичным для DCOM выдавать себя за №2, потому что это то, что я явно указал в качестве моих учетных данных. Но почему вторая попытка входа в систему затем?

Может кто-нибудь объяснить, как работает олицетворение,, а также если есть способ просто игнорировать его и запускать как пользователь, указанный в dcomcnfg?

Ответы

Ответ 1

Отвечая на мой вопрос. После долгих исследований выяснилось, что DCOM имеет два разных случая идентификации:

  • Авторизация создания объекта (CoCreateInstanceEx)
  • Авторизация вызовов методов.

По неизвестным причинам # 2 не наследует настройки №1. По умолчанию он использует учетные данные клиентского процесса и, следовательно, странные логины.

Существует два способа указать учетные данные для # 2. Первый из них является CoSetProxyBlanket. Он устанавливает учетные данные только для указанного прокси (marshaller-unmarshaller):

CoCreateInstanceEx(IID_IObject1, /*login, pass*/, obj1); //Success!
//Logged in and recevied IObject1 proxy in obj1

obj1->DoSomething();
//IObject1 proxy in obj1 now tries to login under process credentials.
//Failure! E_ACCESSDENIED

CoSetProxyBlanket(obj1, /*login, pass*/); //Success!
//IObject1 proxy is now authorized.

obj1->DoSomething(); //Success!
obj1->QueryInterface(IID_IObject2, obj2); //Success!

obj2->DoSomethingElse(); //Failure!
//This different proxy for IObject2 have not yet been authorized.

CoSetProxyBlanket(obj2, /*login, pass*/);
//etc.

Это важно отметить, что в то время как CoCreateInstanceEx требует уровень олицетворения, чтобы быть по крайней мере IMPERSONATE, CoSetProxyBlanket, кажется, не работает ни на что, кроме ОПРЕДЕЛИТЬ.

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

CoInitializeSecurity(/* login, pass */);
CoCreateInstanceEx(IID_IUnknown, /*login, pass*/, obj); //Success!
obj->DoSomething(); //Success!

При использовании CoInitializeSecurity на клиенте вы должны указать asAuthSvc, хотя MSDN говорит, что вы этого не делаете.

Недостаток этого метода является то, очевидно, что если у вас есть несколько объектов DCOM из разных компьютеров вы будете иметь, чтобы указать все учетные данные в этом вызове, и те, которые, вероятно, будут судить против каждого компьютера каждый раз, когда вы открываете другой прокси.

Он также не является надежным, когда вы работаете из DLL (что, если процесс имеет разную защиту по умолчанию?). Таким образом, вероятно, лучше реализовать оболочку QueryInterface, которая CoSetsProxyBlanket перед возвратом с каждого вызова.

Ответ 2

Для тех, кто работает в Delphi, есть одна маленькая заметка, которая может сэкономить много времени. После выполнения операции obj as ISomeInterface вы должны вызвать CoSetProxyBlanket для нового экземпляра. Это может быть не очень очевидным, но все мы знаем, что оператор as вызывает метод QueryInterface, и он может возвращать новый экземпляр.