title: Kerberos Impersonation using SSPI: no errors, but not working
body: <p>I need to impersonate a user in a Java application server, and execute an http request to an ASP application in IIS with the permissions of that user. I am trying to adapt the WindowsNegotiateScheme class of the Apache HttpClient for this purpose. This uses JNA to directly access the Windows SSPI authentication functions, hence the problem is probably not specific to Java. As I need a "protocol transition", I do not have the password of the user to impersonate available, just the name. But using the S4U features, this should be possible.</p>
  
  <p>All SSPI function calls either return 0 (success) or 590610 (SEC_I_CONTINUE_NEEDED), never an error code, and the code runs, but the http request arrives at the ASP side with the user running the application server, no matter if I run it locally on my workstation under my user id, or in the application server (which happens to be the machine also running the IIS/ASP application), which runs as local system.</p>
  
  <p>The relevant part of the code is below:</p>
  
  <pre><code>public Header authenticate(Credentials credentials, HttpRequest request, HttpContext context) throws AuthenticationException {
      final String response;
      if (clientCred == null) { // first time
          boolean impersonate = ...; // logic not relevant here
  
          try {
              final String username = impersonate ? credentials.getUserPrincipal().getName() : CurrentWindowsCredentials.getCurrentUsername();
              final Sspi.TimeStamp lifetime = new Sspi.TimeStamp();
              this.clientCred = new Sspi.CredHandle();
              int credUse = impersonate ? (Sspi.SECPKG_CRED_OUTBOUND | Sspi.SECPKG_CRED_INBOUND) : Sspi.SECPKG_CRED_OUTBOUND;
  
              int rc = Secur32.INSTANCE.AcquireCredentialsHandle(username,
                      scheme, credUse, null, null, null, null,
                      clientCred, lifetime);
              if (WinError.SEC_E_OK != rc) {
                  throw new Win32Exception(rc);
              }
  
              if(impersonate) {
                  impersonationContext = getLocalContext(clientCred);
                  rc = Secur32.INSTANCE.ImpersonateSecurityContext(impersonationContext);
              } else {
                  impersonationContext = null;
              }
  
              String targetSpn = getServicePrincipalName(context);
              response = getToken(null, null, targetSpn);
          } catch (RuntimeException ex) {
              ...
          }
      } else {
          ... // second round
      }
      ... // build header form response and return it
  }
  </code></pre>
  
  <p>Method <code>getLocalContext()</code> is meant to get a context to the service itself for the impersonating user. It looks as follows:</p>
  
  <pre><code>private static Sspi.CtxtHandle getLocalContext(final Sspi.CredHandle initialCredentials) {
      final IntByReference attr = new IntByReference();
  
      String localSpn = "HOST/" + Kernel32Util.getComputerName().toLowerCase();
  
      Sspi.CtxtHandle clientContext = null;
      Sspi.CtxtHandle serverContext = null;
  
      Sspi.SecBufferDesc clientToServerToken;
      Sspi.SecBufferDesc serverToClientToken = null;
      while(true) {
          // get clientContext and clientToServerToken:
          Sspi.CtxtHandle outClientContext = (clientContext != null) ? clientContext : new Sspi.CtxtHandle();
          clientToServerToken = new Sspi.SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE);
          int rc = Secur32.INSTANCE.InitializeSecurityContext(initialCredentials,
                  clientContext, localSpn, Sspi.ISC_REQ_DELEGATE | Sspi.ISC_REQ_MUTUAL_AUTH, 0,
                  Sspi.SECURITY_NATIVE_DREP, serverToClientToken, 0, outClientContext, clientToServerToken,
                  attr, null);
          clientContext = outClientContext; // make them same for next round
          switch (rc) {
              case WinError.SEC_I_CONTINUE_NEEDED:
                  break;
              case WinError.SEC_E_OK:
                  return serverContext != null ? serverContext : clientContext;
              default:
                  freeContexts(clientContext, serverContext);
                  throw new Win32Exception(rc);
          }
  
          // get serverContext and serverToClientToken:
          Sspi.CtxtHandle outServerContext = (serverContext != null) ? serverContext : new Sspi.CtxtHandle();
          serverToClientToken = new Sspi.SecBufferDesc(Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE);
          rc = Secur32.INSTANCE.AcceptSecurityContext(initialCredentials,
                  serverContext, clientToServerToken, Sspi.ISC_REQ_DELEGATE | Sspi.ISC_REQ_MUTUAL_AUTH,
                  Sspi.SECURITY_NATIVE_DREP, outServerContext, serverToClientToken, attr, null);
          serverContext = outServerContext; // make them same for next round
          switch (rc) {
              case WinError.SEC_I_CONTINUE_NEEDED:
                  break;
              case WinError.SEC_E_OK:
                  return serverContext;
              default:
                  freeContexts(clientContext, serverContext);
                  throw new Win32Exception(rc);
          }
      }
  }
  </code></pre>
  
  <p>I found that it runs two complete rounds (<code>InitializeSecurityContext</code>, <code>AcceptSecurityContext</code>, <code>InitializeSecurityContext</code>, <code>AcceptSecurityContext</code>) before returning.</p>
  
  <p>And for reference, the <code>getToken</code> method is just copied from the original WinHttpClient <code>WindowsNegotiateScheme</code> class:</p>
  
  <pre><code>String getToken(
          final CtxtHandle continueCtx,
          final SecBufferDesc continueToken,
          final String targetName) {
      final IntByReference attr = new IntByReference();
      final SecBufferDesc token = new SecBufferDesc(
              Sspi.SECBUFFER_TOKEN, Sspi.MAX_TOKEN_SIZE);
  
      sspiContext = new CtxtHandle();
      final int rc = Secur32.INSTANCE.InitializeSecurityContext(clientCred,
              continueCtx, targetName, Sspi.ISC_REQ_DELEGATE | Sspi.ISC_REQ_MUTUAL_AUTH, 0,
              Sspi.SECURITY_NATIVE_DREP, continueToken, 0, sspiContext, token,
              attr, null);
      switch (rc) {
          case WinError.SEC_I_CONTINUE_NEEDED:
              continueNeeded = true;
              break;
          case WinError.SEC_E_OK:
              dispose(); // Don't keep the context
              continueNeeded = false;
              break;
          default:
              dispose();
              throw new Win32Exception(rc);
      }
      return Base64.encodeBase64String(token.getBytes());
  }
  </code></pre>
  
  <p>I checked in the debugger that the username used for the impersonation is in fact something like <code>DOMAINNAME\johndoe</code>, and I realized it does not even matter if I use a nonexistig user, the behavior is just the same: <code>AcquireCredentialsHandle</code> returns 0 (= SEC_E_OK). This tells me that either this method does not check the user name against the domain controller, but only the syntax of its arguments, but then why does the initial call to <code>InitializeSecurityContext</code> not complain if it first argument are credentials for a nonexistent user? Or the method somehow ignores its first argument at all, but why?</p>
  
  <p>Assuming I have a context for the user to impersonate already from the <code>AcquireCredentialsHandle</code> call and omitting the call to <code>getLocalContext</code> and <code>ImpersonateSecurityContext</code> did not work either.</p>
  
  <p>I found there is not much documentation on pure SSPI usage for impersonation. Most samples I found (like <a href="https://msdn.microsoft.com/en-us/library/ff649265.aspx" rel="nofollow noreferrer">this</a>) rely on a .net class <code>WindowsIdentity</code>, which is not available in the pure SSPI interface. And the <a href="http://referencesource.microsoft.com/#mscorlib/system/security/principal/windowsidentity.cs" rel="nofollow noreferrer">source code</a> of that class does not look as if it just consists of a few SSPI calls, but a lot of other .net infrastructure is referenced.</p>
  
  <p>I thought that maybe the Windows OS impersonation does not collaborate properly with Java, but according to <a href="https://stackoverflow.com/questions/28023447/is-impersonation-in-win32-per-thread">this question</a>, the Windows impersonation is per thread, and according to <a href="https://stackoverflow.com/questions/14648104/how-is-java-concurrency-implemented-for-windows/14648345#14648345">this question</a>, Java uses the OS threads.</p>
  
  <p>How can I get the impersonation working, or do you at least have some hints what to try?</p>