Getting handles to the credentials of a domain user from a process launched by a local user on Windows 10

823 views Asked by At

Background:

  • Computer mycomputer is running Windows 10 and is joined to domain mydomain.com.
  • A user is logged with local account mycomputer\localuser on mycomputer.
  • The user also knows the password of domain account mydomain\domainuser.
  • The service principal name myprotocol/domainuser is registered in Active Directory and maps to domain account mydomain\domainuser.
  • Local user 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.

  • I tried modifying the first argument of that call to "myprotocol/domainuser", "domainuser", "mydomain\domainuser" or even "[email protected]".
  • I tried adding the required credentials in Control Panel's Credential Manager using hostname 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;}}
1

There are 1 answers

0
Drake Wu On BEST ANSWER

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.
    );
}