We are developing Java web application, it will be deployed and used in the pure Windows platform. Our use case is, couple of licensed Tools/Software in Server needs to be accessed by the associates who has license to those tools.
Our plan is to use the Waffle SSO to get the User identity and impersonate the logged in user into the server and access the Tools as a sub process in the impersonated security context.
After lots of reading, we decided to go with createProcessAsUser using Waffle and JNA. We are completely new to the JNA and Waffle. Our blockers and questions as follows,
We are following this thread Create process as different user with Java and Kerberos
How do we get the token bytes from the security context or authentication object?
final byte[] tokenBuffer = authorizationHeader.getTokenBytes();
Because in Spring controller method authorizationHeader
won’t be available to extract the token bytes
Are token bytes from
authorizationHeader
andSID
from user principal the same ?How do we extract the
hToken
(win32.WinNT.HANDLE
) from the token orwindowsidentity
Object (We have tried to get the pointer from thewindowsidentity
to create aHANDLE
object. Since it is private final object, it has failed.)Why hToken (win32.WinNT.HANDLE is referred as security context token. Neither it is token nor it is available in security context
Though
createProcessAsUser
code has many issues, it got executed and created a new process. Only impersonation failed. Whoever accesses this controller method, process is created by server userTo impersonate the user in the server, is the following code enough?
@Bean
public NegotiateSecurityFilter waffleNegotiateSecurityFilter(
SecurityFilterProviderCollection securityFilterProviderCollection, final HttpServletRequest request,
final HttpServletResponse response) throws IOException {
NegotiateSecurityFilter negotiateSecurityFilter = new NegotiateSecurityFilter();
negotiateSecurityFilter.setProvider(securityFilterProviderCollection);
negotiateSecurityFilter.setImpersonate(true);
return negotiateSecurityFilter;
}
This configuration hasn’t impersonated the logged on user in server.
Spring security Configurations:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private NegotiateSecurityFilter negotiateSecurityFilter;
@Autowired
private NegotiateSecurityFilterEntryPoint entryPoint;
@Bean
public CustomNegotiateSecurityFilter customNegotiateSecurityFilter() {
return new CustomNegotiateSecurityFilter();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().authorizeRequests().anyRequest().authenticated().and().httpBasic()
.authenticationEntryPoint(entryPoint).and()
.addFilterBefore(negotiateSecurityFilter, BasicAuthenticationFilter.class)
.addFilterAfter(customNegotiateSecurityFilter(), BasicAuthenticationFilter.class);
}
@Override
@Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication();
}
Waffle Configuration:
@Configuration
public class WaffleConfig {
@Bean
public WindowsAuthProviderImpl waffleWindowsAuthProvider() {
return new WindowsAuthProviderImpl();
}
@Bean
public NegotiateSecurityFilterProvider negotiateSecurityFilterProvider(
WindowsAuthProviderImpl windowsAuthProvider) {
return new NegotiateSecurityFilterProvider(windowsAuthProvider);
}
@Bean
public BasicSecurityFilterProvider basicSecurityFilterProvider(WindowsAuthProviderImpl windowsAuthProvider) {
return new BasicSecurityFilterProvider(windowsAuthProvider);
}
@Bean
public SecurityFilterProviderCollection waffleSecurityFilterProviderCollection(
NegotiateSecurityFilterProvider negotiateSecurityFilterProvider,
BasicSecurityFilterProvider basicSecurityFilterProvider) {
SecurityFilterProvider[] securityFilterProviders = { negotiateSecurityFilterProvider,
basicSecurityFilterProvider };
return new SecurityFilterProviderCollection(securityFilterProviders);
}
@Bean
public NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint(
SecurityFilterProviderCollection securityFilterProviderCollection) {
NegotiateSecurityFilterEntryPoint negotiateSecurityFilterEntryPoint = new NegotiateSecurityFilterEntryPoint();
negotiateSecurityFilterEntryPoint.setProvider(securityFilterProviderCollection);
return negotiateSecurityFilterEntryPoint;
}
@Bean
public NegotiateSecurityFilter waffleNegotiateSecurityFilter(
SecurityFilterProviderCollection securityFilterProviderCollection, final HttpServletRequest request,
final HttpServletResponse response) throws IOException {
NegotiateSecurityFilter negotiateSecurityFilter = new NegotiateSecurityFilter();
negotiateSecurityFilter.setProvider(securityFilterProviderCollection);
negotiateSecurityFilter.setImpersonate(true);
return negotiateSecurityFilter;
}
// This is required for Spring Boot so it does not register the same filter
// twice
@SuppressWarnings({ "rawtypes", "unchecked" })
@Bean
public FilterRegistrationBean waffleNegotiateSecurityFilterRegistration(
NegotiateSecurityFilter waffleNegotiateSecurityFilter, HttpServletRequest request,
HttpServletResponse response) throws IOException {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(waffleNegotiateSecurityFilter);
registrationBean.setEnabled(false);
return registrationBean;
}
}
##Spring Controller method to be invoked by remote users##
@GetMapping("createProcessAsUser")
public String createProcessAsUser() {
SecurityContext context = SecurityContextHolder.getContext();
Authentication authentication = context.getAuthentication();
WindowsPrincipal principal = (WindowsPrincipal) authentication.getPrincipal();
final byte[] tokenBuffer = principal.getSid();
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);
//Advapi32.INSTANCE.ImpersonateLoggedOnUser(HANDLE hToken); --- Not sure how to get the HANDLE for the current User
Advapi32.INSTANCE.ImpersonateSelf(SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation); // Not working
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);
WinBase.STARTUPINFO startupInfo = new WinBase.STARTUPINFO();
WinBase.PROCESS_INFORMATION processInfo = new WinBase.PROCESS_INFORMATION();
boolean createProcessResult = Advapi32.INSTANCE.CreateProcessAsUser(
primaryToken.getValue(), /* hToken */
"C:\\Windows\\notepad.exe", /* lpApplicationName */
null, /* lpCommandLine */
null, /* lpProcessAttributes */
null, /* lpThreadAttributes */
false, /* bInheritHandles */
WinNT.CREATE_NEW_CONSOLE | WinNT.CREATE_UNICODE_ENVIRONMENT, /* dwCreationFlags */
null, /* lpEnvironment */
null, /* lpCurrentDirectory */
startupInfo, /* lpStartupInfo */
processInfo); /* lpProcessInformation */
System.out.println(createProcessResult);
return String.format("New Process created ",createProcessResult);
}