Create process as different user with Java and Kerberos
Asked Answered
T

0

3

I am working on a Java server application (running under Windows as a service) and want to achieve the following scenario:

  • A user issues a POST request to the server. The user is authenticated with Kerberos (SPNEGO, SSO in an enterprise environment).
  • The service creates a new Java process. The process must run as the authenticated user and not the service user (impersonation/delegation).
  • The new process should execute in the security context of the user. It must communicate with other remote systems that require Kerberos authentication (e.g. file shares, other web services, ...).

Working proof-of-concept:

  • Spring Boot application with Waffle for the Kerberos authentication.
  • Create a new process for the authenticated user. I use JNA and the Windows native function CreateProcessAsUser.
  • The process is created as the authenticated user. I can verify this with the Process Explorer utility.

What is missing and not working:

  • The process cannot request other Kerberos tickets (e.g. by calling InitializeSecurityContext())
  • The process cannot access network shares.
  • The process cannot communicate with other web services that require Kerberos authentication.

My questions:

  • What am I missing or what is possibly wrong with my code?
  • Is it even possible to implement what I want to achieve?

Server - Authentication (shortened, extracted from Waffle):

final byte[] tokenBuffer = authorizationHeader.getTokenBytes();
CredHandle serverCredHandle = new CredHandle();
TimeStamp clientLifetime = new TimeStamp();
int rc = Secur32.INSTANCE.AcquireCredentialsHandle(
                                null, 
                                "Negotiate", 
                                Sspi.SECPKG_CRED_INBOUND, 
                                null, 
                                null, 
                                null, 
                                null, 
                                serverCredHandle, 
                                clientLifetime);

SecBufferDesc pbClientToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, tokenBuffer);
CtxtHandle phNewServerContext = new CtxtHandle();
SecBufferDesc pbServerToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE);
IntByReference pfClientContextAttr = new IntByReference();

rc = Secur32.INSTANCE.AcceptSecurityContext(
                                serverCredHandle, 
                                null, 
                                pbClientToken, 
                                Sspi.ISC_REQ_CONNECTION, 
                                Sspi.SECURITY_NATIVE_DREP, 
                                phNewServerContext, 
                                pbServerToken, 
                                pfClientContextAttr, 
                                null);


rc = Advapi32.INSTANCE.ImpersonateLoggedOnUser(/* provide the security context token */)

Server - CreateProcessAsUser:

// get impersonation token of user
WinNT.HANDLEByReference threadToken = new WinNT.HANDLEByReference();
WinNT.HANDLE threadHandle = Kernel32.INSTANCE.GetCurrentThread();

boolean threadTokenResult = Advapi32.INSTANCE.OpenThreadToken(
        threadHandle,
        WinNT.TOKEN_QUERY | WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_ASSIGN_PRIMARY,
        false, /* TRUE if the access check is to be made against the process-level security context. FALSE if the access check is to be made against the current security context of the thread calling the OpenThreadToken function. */
        threadToken);

// create primary token by duplicating impersonation token
WinNT.HANDLEByReference primaryToken = new WinNT.HANDLEByReference();

boolean primaryTokenResult = Advapi32.INSTANCE.DuplicateTokenEx(
        threadToken.getValue(),                                     /* hExistingToken */
        WinNT.TOKEN_DUPLICATE | WinNT.TOKEN_QUERY | WinNT.TOKEN_ASSIGN_PRIMARY, /* dwDesiredAccess */
        null,                                           /* lpTokenAttributes */
        WinNT.SECURITY_IMPERSONATION_LEVEL.SecurityDelegation,      /* ImpersonationLevel */
        WinNT.TOKEN_TYPE.TokenPrimary,                              /* TokenType */
        primaryToken);                                              /* phNewToken */

String environment = createEnvironment(primaryToken);

WinBase.STARTUPINFO startupInfo = new WinBase.STARTUPINFO();
WinBase.PROCESS_INFORMATION processInfo = new WinBase.PROCESS_INFORMATION();

boolean createProcessResult = Advapi32.INSTANCE.CreateProcessAsUser(
        primaryToken.getValue(),    /* hToken */
        null,           /* lpApplicationName */
        command,                    /* lpCommandLine */
        null,       /* lpProcessAttributes */
        null,           /* lpThreadAttributes */
        false,          /* bInheritHandles */
        WinNT.CREATE_NEW_CONSOLE | WinNT.CREATE_UNICODE_ENVIRONMENT, /* dwCreationFlags */
        environment,                /* lpEnvironment */
        processDirectory,           /* lpCurrentDirectory */
        startupInfo,                /* lpStartupInfo */
        processInfo);               /* lpProcessInformation */
Trelu answered 11/9, 2018 at 12:29 Comment(3)
Did you solve this? I'm in a similar situation, trying to generate a kerberos token for a request impersonating a user, but have made no progress in 3 daysGranddaddy
I solved parts of it, see also my other post: stackoverflow.com/questions/52308063 However, Kerberos Delegation is not working properly in all cases. The code above should spawn a new process.Trelu
@Trelu I have followed the above code. I have used Waffle+Spring security to authenticate users and instead of openThread function (returns invalid handle), I have been suggested to openProcess and everything else remains the same. new process has been created but process owner is server user not the impersonated user. Any help or pointers would be grateful. TIA! My implementation, #73123118Theorize

© 2022 - 2024 — McMap. All rights reserved.