#c #winapi #kerberos #sspi #credential-manager
#c #winapi #kerberos #sspi #диспетчер учетных данных
Вопрос:
Предыстория:
- Компьютер
mycomputer
работает под управлением Windows 10 и подключен к доменуmydomain.com
. - Пользователь входит в систему с включенной локальной учетной
mycomputerlocaluser
записьюmycomputer
. - Пользователь также знает пароль учетной записи домена
mydomaindomainuser
. - Имя участника службы
myprotocol/domainuser
зарегистрировано в Active Directory и сопоставляется с учетной записью доменаmydomaindomainuser
. - Локальному пользователю
mycomputerlocaluser
не разрешено запускать процесс какmydomaindomainuser
.
Пользователь хочет запустить серверный процесс под локальной учетной записью, который затем будет использовать учетную запись домена для проверки подлинности входящих подключений с помощью Kerberos.
Я хочу написать код этого сервера.
Клиентский код:
Клиентский код прост и состоит из вызова AcquireCredentialsHandle
, за которым следует вызов InitializeSecurityContext
:
AcquireCredentialsHandle(
nullptr,
"Kerberos",
SECPKG_CRED_OUTBOUND,
nullptr,
nullptr,
nullptr,
nullptr,
amp;credentials,
amp;lifetime);
InitializeSecurityContext(
amp;credentials,
nullptr,
"myprotocol/myport",
ISC_REQ_CONFIDENTIALITY,
0,
SECURITY_NATIVE_DREP,
nullptr,
0,
amp;securityContext,
amp;outBufferArray,
amp;contextAttributes,
amp;lifetime);
Обратите внимание на упрощенное использование строк во фрагментах кода. Реальность, с которой приходится иметь дело, wchar_t
и const
правильность несколько уродливее.
Также обратите внимание, что этот код работает при запуске локальным пользователем, если соответствующие учетные данные хранятся в диспетчере учетных данных панели управления, т.Е. с именем хоста domainuser
(sic.)
Серверный код:
У меня уже есть код, который работает, когда процесс запускается mydomaindomainuser
:
AcquireCredentialsHandle(
nullptr,
"Kerberos",
SECPKG_CRED_INBOUND,
nullptr,
nullptr,
nullptr,
nullptr,
amp;credentials,
amp;lifetime);
AcceptSecurityContext(
amp;credentials,
nullptr,
amp;inBufferArray,
attribs,
SECURITY_NATIVE_DREP,
amp;securityContext,
nullptr,
amp;attribs,
amp;lifetime);
Но когда сервер запускается mycomputerlocaluser
, вызов AcquireCredentialsHandle
завершается ошибкой с кодом SEC_E_NO_CREDENTIALS
.
- Я попытался изменить первый аргумент этого вызова на
"myprotocol/domainuser"
,"domainuser"
,"mydomaindomainuser"
или даже"domainuser@mydomain.com"
. - Я попытался добавить необходимые учетные данные в диспетчер учетных данных панели управления, используя hostname
mycomputer
и evendomainuser
.
Что я могу сделать, чтобы получить дескриптор учетных mydomaindomainuser
данных в процессе, запущенном mycomputerlocaluser
?
Компиляция фрагмента кода:
#include <string>
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define SECURITY_WIN32
#include <sspi.h>//Requires linking on Secur32.lib
int main(){
CredHandle credentials;
TimeStamp lifetime;
std::string package="Kerberos";
std::string principal="myprotocol/domainuser";
auto res=AcquireCredentialsHandle(
principal.data(),
package.data(),
SECPKG_CRED_INBOUND,
nullptr,
nullptr,
nullptr,
nullptr,
amp;credentials,
amp;lifetime);
if(res==SEC_E_OK){
std::printf("Successn");
FreeCredentialsHandle(amp;credentials);
return 0;}
else{
std::printf("Failuren");
return res;}}
Комментарии:
1. Как вы настраиваете учетные данные? Это почти всегда будет причиной SEC_E_NO_CREDENTIALS . Также немного странно, что ACH возвращает это. Обычно его возвращает ASC или ISC, поскольку ACH мало что делает под прикрытием.
2. @Steve я не уверен, что понимаю ваш вопрос. Пользователь входит в Windows, используя свою локальную учетную запись, а затем запускает серверный код, который выполняет приведенный выше фрагмент кода.
AcquireCredentialsHandle
предполагается инициализироватьcredentials
, но это не удается из-заSECPKG_CRED_INBOUND
. На стороне клиента тот же вызов withSECPKG_CRED_OUTBOUND
работает отлично.3. Извините, делал это из памяти и неправильно запомнил параметры. Вы передаете null authdata (параметр 5) при исходящем вызове ACH, поэтому он должен полагаться на локальные учетные данные по умолчанию. Однако учетные данные локального пользователя не поддерживают единый вход таким образом, поскольку он не знает, в какой области вы находитесь.
4. Проверьте этот пример , чтобы получить учетные данные для конкретной учетной записи пользователя.
5. @DrakeWu-MSFT Это работает. Пожалуйста, напишите ответ со ссылкой. Я проголосую.
Ответ №1:
Чтобы получить учетные данные, отличные от тех, которые связаны с текущим сеансом входа в систему, заполните SEC_WINNT_AUTH_IDENTITY
структуру информацией для альтернативного участника безопасности. Передайте структуру AcquireCredentialsHandle
функции с помощью pAuthData
параметра.
И этот пример micrsoft демонстрирует вызов на стороне клиента для получения дайджеста учетных данных для конкретной учетной записи пользователя:
#include <windows.h>
#ifdef UNICODE
ClientAuthID.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
#else
ClientAuthID.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
#endif
void main()
{
SECURITY_STATUS SecStatus;
TimeStamp tsLifetime;
CredHandle hCred;
SEC_WINNT_AUTH_IDENTITY ClientAuthID;
LPTSTR UserName = TEXT("ASecurityPrinciple");
LPTSTR DomainName = TEXT("AnAuthenticatingDomain");
// Initialize the memory.
ZeroMemory( amp;ClientAuthID, sizeof(ClientAuthID) );
// Specify string format for the ClientAuthID structure.
// Specify an alternate user, domain and password.
ClientAuthID.User = (unsigned char *) UserName;
ClientAuthID.UserLength = _tcslen(UserName);
ClientAuthID.Domain = (unsigned char *) DomainName;
ClientAuthID.DomainLength = _tcslen(DomainName);
// Password is an application-defined LPTSTR variable
// containing the user password.
ClientAuthID.Password = Password;
ClientAuthID.PasswordLength = _tcslen(Password);
// Get the client side credential handle.
SecStatus = AcquireCredentialsHandle (
NULL, // Default principal.
WDIGEST_SP_NAME, // The Digest SSP.
SECPKG_CRED_OUTBOUND, // Client will use the credentials.
NULL, // Do not specify LOGON id.
amp;ClientAuthID, // User information.
NULL, // Not used with Digest SSP.
NULL, // Not used with Digest SSP.
amp;hCred, // Receives the credential handle.
amp;tsLifetime // Receives the credential time limit.
);
}