c++winapikerberossspicredential-manager

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


Background:

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.

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

Solution

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