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

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

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

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

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

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

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

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

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

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

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

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

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

  3. пользователь, под учетными данными которого работает ClientApp клиентский компьютер.

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

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

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

2 ответов


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

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

по неизвестным причинам #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 требует, чтобы уровень олицетворения был по крайней мере олицетворением, CoSetProxyBlanket, похоже, не работает ни над чем, кроме идентификации.

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

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

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

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

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


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