Выпуск:Вызов 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-вызова?
Это не помогает. Как я могу вызвать QueryInterface с другим контекстом безопасности, чем этот процесс? Например, как я могу создать программу, которая подключалась к нескольким серверам OPC, используя разные учетные данные для каждого? – bruceceng
Только примечание к вашим значениям «under return», Access Denied не упоминается как возможность »: В COM код успешных кодов считается частью контракта интерфейса, а набор кодов ошибок - нет. То есть, интерфейсный метод разрешает возвращать ЛЮБЫЕ ошибки, которые он хочет, даже если они не документированы, не нарушая «контракт интерфейса». Существует много кодов ошибок (например, RPC_xxxx), которые могут возвращать почти любой метод (D) COM, и они не документируются с каждым методом. Задокументированные коды ошибок являются, таким образом, «для иллюстрации», те, которые заслуживают объяснения, но не более того. – ZbynekZ
У вас нет большого контроля над контекстом безопасности в вызове QuerryInterface. Возможно, вы можете реализовать пользовательский суррогат и настроить CoInitializeSecurity в суррогате, но у вас будет один контекст безопасности для каждого суррогата. Подробнее о пользовательских суррогатах здесь: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682432(v=vs.85).aspx –