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 */