Выбрать главу

Неявные вызовы CoInitializeSecurity используют для второго и третьего параметров (cAuthSvc и rgsAuthSvc) значения -1 и нуль соответственно, указывая тем самым, что должны использоваться модули защиты, принятые по умолчанию. Неявные вызовы CoInitializeSecurity находят значения для пятого и шестого параметров (dwAuthnLevel и dwImpLevel) в следующем элементе реестра всей машины:

[HKEY_LOCAL_MACHINE\Software\Microsoft\OLE]

LegacyAuthenticationLevel = 0x5

LegacyImpersonationLevel = 0x3

RPC_C_AUTHN_LEVEL_PKT_INTEGRITY и RPC_C_AUTHN_LEVEL_IMPERSONATE принимают числовые значения 5 и 3 соответственно. Если эти именованные величины отсутствуют, то используются значения RPC_C_AUTHN_LEVEL_CONNECT и RPC_C_IMP_LEVEL_IDENTIFY. Из флагов, используемых в восьмом параметре функций CoInitializeSecurity, dwCapabilities, в настоящее время читается из элемента реестра всей машины только флаг EOAC_SECURE_REFS:

[HKEY_LOCAL_MACHINE\Software\Microsoft\OLE]

LegacySecureRefs = "Y"

Если эта именованная величина в наличии и содержит "Y" или "y", то COM будет использовать флаг EOAC_SECURE_REFS; в противном случае используется флаг EOAC_NONE. Каждая из этих традиционных установок аутентификации может быть легко изменена при помощи DCOMCNFG.ЕХЕ.

Программируемая защита

Установки, сделанные при помощи CoInitializeSecurity , называются автоматическими установками защиты, поскольку они автоматически применяются ко всем маршалированным объектным ссылкам. Часто бывает, что небольшому числу объектных ссылок необходимо использовать установки защиты, которые отличаются от установок по умолчанию для всего процесса. Наиболее часто встречающийся сценарий таков: для повышения производительности используется довольно низкий уровень аутентификации, но необходимо зашифровать один определенный интерфейс. Вместо того чтобы принудительно использовать шифрование во всем процессе, предпочтительно применить его к тем объектным ссылкам, для которых это необходимо.

Чтобы позволить разработчикам игнорировать автоматические установки защиты на базе интерфейсных заместителей, администратор заместителей выставляет интерфейс IClientSecurity:

[local, object, uuid(0000013D-0000-0000-C000-000000000046)]

interface IClientSecurity : IUnknown {

// get security settings for interface proxy pProxy

// получаем установки защиты для интерфейсного заместителя

pProxy HRESULT* QueryBlanket([in] IUnknown *pProxy, [out] DWORD *pAuthnSvc, [out] DWORD *pAuthzSvc, [out] OLECHAR **pServerPrincName, [out] DWORD *pAuthnLevel, [out] DWORD *pImpLevel, [out] void **pAuthInfo, [out] DWORD *pCapabilities );

// change security settings for interface proxy pProxy

// изменяем установки защиты для интерфейсного заместителя

pProxy HRESULT SetBlanket([in] IUnknown *pProxy, [in] DWORD AuthnSvc, [in] DWORD AuthzSvc, [in] OLECHAR *pServerPrincName, [in] DWORD AuthnLevel, [in] DWORD ImpLevel, [in] void *pAuthInfo, [in] DWORD Capabilities );

// duplicate an interface proxy

// дублируем интерфейсный заместитель

HRESULT CopyProxy([in] IUnknown *pProxy, [out] IUnknown **ppCopy );

}

Второй, третий и четвертый параметры методов SetBlanket и QueryBlanket соответствуют трем членам структуры данных SOLE_AUTHENTICATION_SERVICE. Под Windows NT 4.0 единственными допустимыми величинами являются соответственно RPC_C_AUTHN_WINNT, RPC_C_AUTHN_NONE и нуль.

Как показано на рис. 6.1, каждый отдельный интерфейсный заместитель имеет свои собственные установки защиты. Метод IClientSecurity::SetBlanket позволяет вызывающей программе изменять эти установки для каждого интерфейсного заместителя по отдельности.

Метод IClientSecurity::QueryBlanket позволяет вызывающей программе прочитать эти установки для отдельного интерфейсного заместителя. В качестве параметров, о которых вызывающая программа не беспокоится, могут быть переданы нулевые указатели. Метод IClientSecurity::СоруРгоху позволяет вызывающей программе копировать интерфейсный заместитель. Это дает возможность делать изменения в копии интерфейса, которая не будет возвращаться при последующих запросах QueryInterface об администраторе заместителей. В идеале установки защиты следовало бы делать только в скопированных интерфейсных заместителях, чтобы изолировать измененный заместитель от нормальной реализации администратора заместителей в QueryInterface ; это также позволило бы нескольким потокам независимо друг от друга изменять полные установки защиты между вызовами метода.

Все параметры методов IClientSecurity::SetBlanket и IClientSecurity::QueryBlanket соответствуют параметрам CoInitializeSecurity с одним значительным исключением. Седьмой параметр (pAuthInfo ) указывает на набор полномочий клиента. Точная форма этих полномочий специфична для каждого модуля безопасности. Для модуля безопасности NTLM этот параметр может указывать на структуру COAUTHIDENTITY:

typedef struct _COAUTHIDENTITY {

OLECHAR *User;

// user account name

// имя учетной записи пользователя

ULONG UserLength;

// wcslen(User)

// длина имени пользователя

OLECHAR *Domain;

// Domain/Machine name

// имя домена/машины

ULONG DomainLength;

// wcslen(Domain)

// длина имени домена

OLECHAR *Password;

// cleartext password

// пароль открытым текстом

ULONG PasswordLength;

// wcslen(Password)

// длина пароля

ULONG Flags;

// must be SEC_WINNT_AUTH_IDENTITY_UNICODE

// должно быть SEC_WINNT_AUTH_IDENTITY_UNICODE

} COAUTHIDENTITY;

Эта структура позволяет клиентам делать вызовы методов COM как любым принципалам защиты, при условии, что они знают открытые тексты паролей для желаемой учетной записи[1]. Если вместо указателя на явную структуру COAUTHIDENTITY передается нулевой указатель, то каждый внешний вызов будет делаться с использованием полномочий вызывающего процесса[2].

Чаще всего метод IClientSecurity::SetBlanket применяется для повышения уровня аутентификации отдельного заместителя. Следующий код демонстрирует эту технологию:

HRESULT Encrypt(IApe *pApe) {

IClientSecurity *pcs = 0;

// ask proxy manager for IClientSecurity interface

// запрашиваем интерфейс IClientSecurity у администратора заместителей

HRESULT hr = pApe->QueryInterface(IID_IClientSecurity, (void**)&pcs);

if (SUCCEEDED(hr)) {

hr = pcs->SetBlanket(pApe, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, 0, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY, 0, EOAC_NONE);

pcs->Release();

}

return hr;

}

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

HRESULT DupeAndEncrypt(IApe *pApe, IApe * &rpSecretApe) {

rpSecretApe = 0;

IClientSecurity *pcs = 0;

// ask proxy manager for IClientSecurity interface

// запрашиваем интерфейс IClientSecurity у администратора заместителей

HRESULT hr = pApe->QueryInterface(IID_IClientSecurity, (void**)&pcs);

if (SUCCEEDED(hr)) {

hr = pcs->CopyProxy(pApe, (IUnknown**)&rpSecretApe);

if (SUCCEEDED(hr))

hr = pcs->SetBlanket (rpSecretApe, RPC_AUUTHN_WINNT, RPC_C_AUTHZ_NONE, 0, RPC_С_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_IMP_LEVEL_IDENTIFY, 0, EOAC_NONE);

pcs->Release();

}

return hr;

}

Для удобства в COM API предусмотрены оберточные функции вокруг каждого из трех методов IClientSecurity , которые изнутри вызывают QueryInterface для нахождения соответствующего интерфейса IClientSecurity и затем вызывают нужный метод:

вернуться

1 Важно отметить, что во время написания этого текста структура COAUTHIDENTITY не поддерживается для связей внутри одной машины. Она работает надежно для удаленных связей с хостом.

вернуться

2 Под Windows NT 5.0 поддержка заимствования прав на уровне делегирования (delegation-level impersonation) может изменить такое поведение, используя маркер вызывающего потока. За дополнительной информацией обращайтесь к имеющейся документации.