2016-09-12 6 views
1

Выпуск:Вызов QueryInterface с пользовательской идентичности

Я успешно называть CoSetProxyBlanket на прокси (если это правильный термин для этого), а затем я называю QueryInterface на тот же прокси-сервер, но я получаю результат 0x80070005 ("Доступ закрыт"). Однако, если я сначала вызову CoInitializeSecurity (которого я пытаюсь избежать) с теми же учетными данными, тогда вызов будет успешным.

Вопрос:

Как я могу успешно получить интерфейс, мне нужно без вызова CoInitializeSecurity? Насколько я понимаю, процесс может вызвать этот метод только один раз, поэтому он несовместим с созданием dll и обычно может быть заменен вызовами CoSetProxyBlanket.

Деталь:

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

Во-первых, я создаю структуру идентичности с доменом, имя пользователя и пароль, которые действительны на сервере:

COAUTHINFO  authInfo; 
COAUTHIDENTITY authIdentity; 

authIdentity.Domain    = (unsigned short *) w_domain; 
authIdentity.DomainLength  = wcslen(w_domain); 
authIdentity.Flags    = SEC_WINNT_AUTH_IDENTITY_UNICODE; 
authIdentity.Password   = (unsigned short *) w_password; 
authIdentity.PasswordLength  = wcslen(w_password); 
authIdentity.User    = (unsigned short *) w_username; 
authIdentity.UserLength   = wcslen(w_username); 

authInfo.dwAuthnLevel   = RPC_C_AUTHN_LEVEL_CALL; 
authInfo.dwAuthnSvc    = RPC_C_AUTHN_WINNT; 
authInfo.dwAuthzSvc    = RPC_C_AUTHZ_NONE; 
authInfo.dwCapabilities   = EOAC_NONE; 
authInfo.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE; 
authInfo.pAuthIdentityData  = &authIdentity; 
authInfo.pwszServerPrincName = NULL; 

ServerInfo.pAuthInfo = &authInfo; 

Тогда я могу позвонить CoCreateInstanceEx с этой информацией сервера получить дескриптор (m_IOPCServer) на мой OPC-сервер (IID_IOPCServer).

После того как я получить ручку, я обнаружил, что необходимо еще раз установить больше разрешений (см How does impersonation in DCOM work?) с этим вызовом:

hr = CoSetProxyBlanket(m_IOPCServer, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, 
     NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, 
     &authIdentity, EOAC_NONE); 

После этого я в состоянии успешно получить дескриптор OPC Item Группа:

hr = m_IOPCServer->AddGroup(L"", FALSE, reqUptRate, clientHandle, 
     NULL, NULL, lcid, &m_hServerGroup, &revisedUptRate, 
     IID_IOPCItemMgt,(LPUNKNOWN*)&m_IOPCItemMgt); 

Однако, когда я пытаюсь использовать этот код:

hr = m_IOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO); 

Результат: 0x80070005 («Доступ запрещен»). Это так, даже если я успешно вызываю CoSetProxyBlanket на m_IOPCItemMgt. Однако, если я сначала вызову CoInitializeSecurity, вызов будет успешным.

Я считаю, что проблема связана с How does impersonation in DCOM work? тем, что функция QueryInterface является формой создания объекта, поэтому она не использует ту же защиту, что и другой метод, например AddGroup. Однако в ссылке Microsoft QueryInterface, в примечаниях к разработчику, это звучит так, как QueryInterface не должно проверять ACL и под значениями возврата, Access Denied не упоминается как возможность. Я не думаю, что эта проблема специфична для реализации, потому что я пробовал свой код на некоторых известных коммерческих серверах OPC (например, Matrikon Simulation Server), а также с открытым исходным кодом LightOPC, который не обеспечивает никакой дополнительной безопасности.

Я предполагаю, что то, что мне нужно сделать, это найти способ повторить эту команду

hr = m_IOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO); 

, но сделать это в то же время поставки authIdentity. Это возможно?Можно ли это сделать с помощью CoCreateInstanceEx или CoGetClassObject или другого COM-вызова?

ответ

1

Не вдаваясь в подробности: CoInitializeSecurity всегда вызывается не реже одного раза в процессе. Это можно сделать неявно или явно. Если ваш код не делает явный вызов, среда выполнения DCOM делает это для вас с параметрами, занесенными из реестра. Вы можете попытаться настроить соответствующие значения реестра, чтобы заставить DCOm использовать значения, аналогичные значениям, используемым в вашем явном вызове. Ключ реестра, который содержит эти значения является «HKEY_LOCAL_MACHINE \ SOFTWARE \ Classes \ AppID {AppID_GUID}» Этот ключ описано здесь: https://msdn.microsoft.com/en-us/library/windows/desktop/ms693736(v=vs.85).aspx

+0

Это не помогает. Как я могу вызвать QueryInterface с другим контекстом безопасности, чем этот процесс? Например, как я могу создать программу, которая подключалась к нескольким серверам OPC, используя разные учетные данные для каждого? – bruceceng

+0

Только примечание к вашим значениям «under return», Access Denied не упоминается как возможность »: В COM код успешных кодов считается частью контракта интерфейса, а набор кодов ошибок - нет. То есть, интерфейсный метод разрешает возвращать ЛЮБЫЕ ошибки, которые он хочет, даже если они не документированы, не нарушая «контракт интерфейса». Существует много кодов ошибок (например, RPC_xxxx), которые могут возвращать почти любой метод (D) COM, и они не документируются с каждым методом. Задокументированные коды ошибок являются, таким образом, «для иллюстрации», те, которые заслуживают объяснения, но не более того. – ZbynekZ

+0

У вас нет большого контроля над контекстом безопасности в вызове QuerryInterface. Возможно, вы можете реализовать пользовательский суррогат и настроить CoInitializeSecurity в суррогате, но у вас будет один контекст безопасности для каждого суррогата. Подробнее о пользовательских суррогатах здесь: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682432(v=vs.85).aspx –

0

Вы должны вызвать CoSetProxyBlanket на каждом новом экземпляре COM объекта, так что в вашем случае вы должны назовите его даже для m_IOPCItemMgt.