2

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,

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 and SID from user principal the same ?

  • How do we extract the hToken (win32.WinNT.HANDLE) from the token or windowsidentity Object (We have tried to get the pointer from the windowsidentity to create a HANDLE 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 user

  • To 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);

  } 
Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63
Chandru
  • 81
  • 1
  • 9
  • Did you consider [this comment](https://stackoverflow.com/questions/52308063/how-to-logon-a-user-on-a-server-and-run-a-process-given-a-kerberos-ticket#comment91801966_52412213)? – Daniel Widdis Jul 21 '22 at 21:01
  • Thanks Daniel! What are the configurations to be made in the server to allow impersonation/Delegation. BTW, Server is already running as Admin. Kindly let me know exactly, what are the configs are required. – Chandru Jul 21 '22 at 23:34
  • Sorry, I know JNA but I don't know impersonation, which is more of a Windows thing. I just found that comment in my attempts to search for a solution for you and thought it might hint you toward a solution. – Daniel Widdis Jul 22 '22 at 00:31

0 Answers0