问题描述
背景:
- 计算机
mycomputer
正在运行Windows 10,并已加入域mydomain.com
。 - 使用
mycomputer\localuser
上的本地帐户mycomputer
登录用户。 - 用户还知道域帐户
mydomain\domainuser
的密码。 - 服务主体名称
myprotocol/domainuser
已在Active Directory中注册,并映射到域帐户mydomain\domainuser
。 - 不允许本地用户
mycomputer\localuser
以mydomain\domainuser
身份启动进程。
用户希望在本地帐户下启动服务器进程,然后该服务器进程将使用域帐户来验证与Kerberos的传入连接。
我要编写该服务器的代码。
客户代码:
客户端代码很简单,包括对AcquireCredentialsHandle
的调用以及对InitializeSecurityContext
的调用:
AcquireCredentialsHandle(
nullptr,"Kerberos",SECPKG_CRED_OUTBOUND,nullptr,&credentials,&lifetime);
InitializeSecurityContext(
&credentials,"myprotocol/myport",ISC_REQ_CONFIDENTIALITY,Security_NATIVE_DREP,&securityContext,&outBufferArray,&contextAttributes,&lifetime);
请注意代码段中字符串的简化用法。必须处理wchar_t
和const
正确性的现实有些丑陋。
还请注意,如果适当的凭据存储在控制面板的凭据管理器中,即主机名为domainuser
(如此),则该代码在由本地用户启动时有效。
服务器代码:
当mydomain\domainuser
启动该过程时,我已经有一个可以使用的代码:
AcquireCredentialsHandle(
nullptr,SECPKG_CRED_INBOUND,&lifetime);
AcceptSecurityContext(
&credentials,&inBufferArray,attribs,&attribs,&lifetime);
但是,当服务器由mycomputer\localuser
启动时,对AcquireCredentialsHandle
的调用失败,代码为SEC_E_NO_CREDENTIALS
。
- 我尝试将该调用的第一个参数修改为
"myprotocol/domainuser"
,"domainuser"
,"mydomain\domainuser"
甚至"domainuser@mydomain.com"
。 - 我尝试使用主机名
mycomputer
甚至domainuser
在控制面板的凭据管理器中添加所需的凭据。
在mydomain\domainuser
启动的过程中如何获取mycomputer\localuser
的凭证句柄?
编译代码段:
#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(),&lifetime);
if(res==SEC_E_OK){
std::printf("Success\n");
FreeCredentialsHandle(&credentials);
return 0;}
else{
std::printf("Failure\n");
return res;}}
解决方法
要获取除与当前登录会话关联的凭据以外的凭据,请在SEC_WINNT_AUTH_IDENTITY
结构中填充备用安全主体的信息。使用AcquireCredentialsHandle
参数将结构传递给pAuthData
函数。
this micrsoft example演示了客户端调用以获取特定用户帐户的摘要凭据:
#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( &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.
&ClientAuthID,// User information.
NULL,// Not used with Digest SSP.
NULL,// Not used with Digest SSP.
&hCred,// Receives the credential handle.
&tsLifetime // Receives the credential time limit.
);
}