Background:
mycomputer
is running Windows 10 and is joined to domain mydomain.com
.mycomputer\localuser
on mycomputer
.mydomain\domainuser
.myprotocol/domainuser
is registered in Active Directory and maps to domain account mydomain\domainuser
.mycomputer\localuser
is not allowed to start a process as mydomain\domainuser
.The user wants to launch a server process under the local account which would then use the domain account to authenticate incoming connections with Kerberos.
I want to write the code of that server.
Client code:
The client code is straightforward and consist of a call to AcquireCredentialsHandle
followed by a call to InitializeSecurityContext
:
AcquireCredentialsHandle(
nullptr,
"Kerberos",
SECPKG_CRED_OUTBOUND,
nullptr,
nullptr,
nullptr,
nullptr,
&credentials,
&lifetime);
InitializeSecurityContext(
&credentials,
nullptr,
"myprotocol/myport",
ISC_REQ_CONFIDENTIALITY,
0,
SECURITY_NATIVE_DREP,
nullptr,
0,
&securityContext,
&outBufferArray,
&contextAttributes,
&lifetime);
Note the simplified usage of strings in the code snippets. The reality which have to deal with wchar_t
and const
correctness is somewhat uglier.
Also note that this code works when launched by a local user if appropriate credentials are stored in Control Panel's Credential Manager - i.e. with hostname domainuser
(sic.)
Server code:
I already have a code which works when the process is launched by mydomain\domainuser
:
AcquireCredentialsHandle(
nullptr,
"Kerberos",
SECPKG_CRED_INBOUND,
nullptr,
nullptr,
nullptr,
nullptr,
&credentials,
&lifetime);
AcceptSecurityContext(
&credentials,
nullptr,
&inBufferArray,
attribs,
SECURITY_NATIVE_DREP,
&securityContext,
nullptr,
&attribs,
&lifetime);
But when the server is launched by mycomputer\localuser
, the call to AcquireCredentialsHandle
fails with code SEC_E_NO_CREDENTIALS
.
"myprotocol/domainuser"
, "domainuser"
, "mydomain\domainuser"
or even "domainuser@mydomain.com"
.mycomputer
and even domainuser
.What can I do to acquire the credential handle of mydomain\domainuser
in a process launched by mycomputer\localuser
?
Compiling code snippet:
#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,
&credentials,
&lifetime);
if(res==SEC_E_OK){
std::printf("Success\n");
FreeCredentialsHandle(&credentials);
return 0;}
else{
std::printf("Failure\n");
return res;}}
To obtain credentials other than those associated with the current logon session, populate a SEC_WINNT_AUTH_IDENTITY
structure with information for the alternate security principal. Pass the structure to the AcquireCredentialsHandle
function using the pAuthData
parameter.
And this micrsoft example demonstrates a client-side call to obtain Digest credentials for a specific user account:
#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.
);
}