public byte[] requestServiceTicket(Subject clientSubject, String servicePrincipalName) {
   // For some reason SPNs in the format HTTP/servicehost.domain end up as
   // HTTP/servicehost.domain/hostname. SPNs defined as [email protected] work fine.
   servicePrincipalName = servicePrincipalName.replace('/', '@');
   try {
     Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
     GSSManager manager = GSSManager.getInstance();
     GSSName serverName =
         manager.createName(servicePrincipalName, GSSName.NT_HOSTBASED_SERVICE, krb5Oid);
     final GSSContext context =
         manager.createContext(serverName, krb5Oid, null, GSSContext.DEFAULT_LIFETIME);
     byte[] serviceTicket =
         Subject.doAs(
             clientSubject,
             new PrivilegedAction<byte[]>() {
               public byte[] run() {
                 byte[] token = new byte[0];
                 // This is a one pass context initialisation.
                 try {
                   context.requestMutualAuth(false);
                   context.requestCredDeleg(false);
                   return context.initSecContext(token, 0, token.length);
                 } catch (GSSException e) {
                   e.printStackTrace();
                   return null;
                 }
               }
             });
     return serviceTicket;
   } catch (GSSException e) {
     e.printStackTrace();
     return null;
   }
 }
 /** Called to invalidate this credential element. */
 public void dispose() throws GSSException {
   try {
     destroy();
   } catch (javax.security.auth.DestroyFailedException e) {
     GSSException gssException =
         new GSSException(
             GSSException.FAILURE, -1, "Could not destroy credentials - " + e.getMessage());
     gssException.initCause(e);
   }
 }
 /**
  * Closes the session. If any {@link GSSContext} is present in the session then it is closed.
  *
  * @param message the error message
  */
 @Override
 protected void closeSession(String message) {
   GSSContext ctx = (GSSContext) getSession().getAttribute(GSS_CONTEXT);
   if (ctx != null) {
     try {
       ctx.dispose();
     } catch (GSSException e) {
       e.printStackTrace();
       super.closeSession(message, e);
       return;
     }
   }
   super.closeSession(message);
 }
  private static KerberosTicket getTgt(int caller, Krb5NameElement name, int initLifetime)
      throws GSSException {

    String realm = null;
    final String clientPrincipal, tgsPrincipal = null;

    /*
     * Find the TGT for the realm that the client is in. If the client
     * name is not available, then use the default realm.
     */
    if (name != null) {
      clientPrincipal = (name.getKrb5PrincipalName()).getName();
      realm = (name.getKrb5PrincipalName()).getRealmAsString();
    } else {
      clientPrincipal = null;
      try {
        Config config = Config.getInstance();
        realm = config.getDefaultRealm();
      } catch (KrbException e) {
        GSSException ge =
            new GSSException(
                GSSException.NO_CRED,
                -1,
                "Attempt to obtain INITIATE credentials failed!" + " (" + e.getMessage() + ")");
        ge.initCause(e);
        throw ge;
      }
    }

    final AccessControlContext acc = AccessController.getContext();

    try {
      final int realCaller = (caller == GSSUtil.CALLER_UNKNOWN) ? GSSUtil.CALLER_INITIATE : caller;
      return AccessController.doPrivileged(
          new PrivilegedExceptionAction<KerberosTicket>() {
            public KerberosTicket run() throws Exception {
              return Krb5Util.getTicket(realCaller, clientPrincipal, tgsPrincipal, acc);
            }
          });
    } catch (PrivilegedActionException e) {
      GSSException ge =
          new GSSException(
              GSSException.NO_CRED,
              -1,
              "Attempt to obtain new INITIATE credentials failed!" + " (" + e.getMessage() + ")");
      ge.initCause(e.getException());
      throw ge;
    }
  }
  @Override
  public Header authenticate(
      final Credentials credentials, final HttpRequest request, final HttpContext context)
      throws AuthenticationException {
    Args.notNull(request, "HTTP request");
    switch (state) {
      case UNINITIATED:
        throw new AuthenticationException(
            getSchemeName() + " authentication has not been initiated");
      case FAILED:
        throw new AuthenticationException(getSchemeName() + " authentication has failed");
      case CHALLENGE_RECEIVED:
        try {
          final HttpRoute route = (HttpRoute) context.getAttribute(HttpClientContext.HTTP_ROUTE);
          if (route == null) {
            throw new AuthenticationException("Connection route is not available");
          }
          HttpHost host;
          if (isProxy()) {
            host = route.getProxyHost();
            if (host == null) {
              host = route.getTargetHost();
            }
          } else {
            host = route.getTargetHost();
          }
          final String authServer;
          String hostname = host.getHostName();

          if (this.useCanonicalHostname) {
            try {
              // TODO: uncomment this statement and delete the resolveCanonicalHostname,
              // TODO: as soon canonical hostname resolving is implemented in the
              // SystemDefaultDnsResolver
              // final DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;
              // hostname = dnsResolver.resolveCanonicalHostname(host.getHostName());
              hostname = resolveCanonicalHostname(hostname);
            } catch (UnknownHostException ignore) {
            }
          }
          if (this.stripPort) { // || host.getPort()==80 || host.getPort()==443) {
            authServer = hostname;
          } else {
            authServer = hostname + ":" + host.getPort();
          }

          if (log.isDebugEnabled()) {
            log.debug("init " + authServer);
          }
          token = generateToken(token, authServer, credentials);
          state = State.TOKEN_GENERATED;
        } catch (final GSSException gsse) {
          state = State.FAILED;
          if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL
              || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) {
            throw new InvalidCredentialsException(gsse.getMessage(), gsse);
          }
          if (gsse.getMajor() == GSSException.NO_CRED) {
            throw new InvalidCredentialsException(gsse.getMessage(), gsse);
          }
          if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN
              || gsse.getMajor() == GSSException.DUPLICATE_TOKEN
              || gsse.getMajor() == GSSException.OLD_TOKEN) {
            throw new AuthenticationException(gsse.getMessage(), gsse);
          }
          // other error
          throw new AuthenticationException(gsse.getMessage());
        }
      case TOKEN_GENERATED:
        final String tokenstr = new String(base64codec.encode(token));
        if (log.isDebugEnabled()) {
          log.debug("Sending response '" + tokenstr + "' back to the auth server");
        }
        final CharArrayBuffer buffer = new CharArrayBuffer(32);
        if (isProxy()) {
          buffer.append(AUTH.PROXY_AUTH_RESP);
        } else {
          buffer.append(AUTH.WWW_AUTH_RESP);
        }
        buffer.append(": Negotiate ");
        buffer.append(tokenstr);
        return new BufferedHeader(buffer);
      default:
        throw new IllegalStateException("Illegal state: " + state);
    }
  }
  void go() throws Exception {
    OneKDC k = new OneKDC(null);
    k.writeJAASConf();

    Files.delete(Paths.get(OneKDC.KTAB));

    // Starts with no keytab
    c = Context.fromJAAS("client");
    s = Context.fromJAAS("com.sun.security.jgss.krb5.accept");

    // Test 1: read new key 1 from keytab
    k.addPrincipal(OneKDC.SERVER, "pass1".toCharArray());
    k.writeKtab(OneKDC.KTAB);
    connect();

    // Test 2: service key cached, find 1 in keytab (now contains 1 and 2)
    k.addPrincipal(OneKDC.SERVER, "pass2".toCharArray());
    k.appendKtab(OneKDC.KTAB);
    connect();

    // Test 3: re-login. Now find 2 in keytab
    c = Context.fromJAAS("client");
    connect();

    // Test 4: re-login, KDC use 3 this time.
    c = Context.fromJAAS("client");
    // Put 3 and 4 into keytab but keep the real key back to 3.
    k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray());
    k.appendKtab(OneKDC.KTAB);
    k.addPrincipal(OneKDC.SERVER, "pass4".toCharArray());
    k.appendKtab(OneKDC.KTAB);
    k.addPrincipal(OneKDC.SERVER, "pass3".toCharArray());
    connect();

    // Test 5: invalid keytab file, should ignore
    try (FileOutputStream fos = new FileOutputStream(OneKDC.KTAB)) {
      fos.write("BADBADBAD".getBytes());
    }
    connect();

    // Test 6: delete keytab file, identical to revoke all
    Files.delete(Paths.get(OneKDC.KTAB));
    try {
      connect();
      throw new Exception("Should not success");
    } catch (GSSException gsse) {
      System.out.println(gsse);
      KrbException ke = (KrbException) gsse.getCause();
      // KrbApReq.authenticate(*) if (dkey == null)...
      // This should have been Krb5.KRB_AP_ERR_NOKEY
      if (ke.returnCode() != Krb5.API_INVALID_ARG) {
        throw new Exception("Not expected failure code: " + ke.returnCode());
      }
    }

    // Test 7: 3 revoked, should fail (now contains only 5)
    k.addPrincipal(OneKDC.SERVER, "pass5".toCharArray());
    k.writeKtab(OneKDC.KTAB); // overwrite keytab, which means
    // old key is revoked
    try {
      connect();
      throw new Exception("Should not success");
    } catch (GSSException gsse) {
      System.out.println(gsse);
      // Since 7197159, different kvno is accepted, this return code
      // will never be thrown out again.
      // KrbException ke = (KrbException)gsse.getCause();
      // if (ke.returnCode() != Krb5.KRB_AP_ERR_BADKEYVER) {
      //    throw new Exception("Not expected failure code: " +
      //            ke.returnCode());
      // }
    }

    // Test 8: an empty KDC means revoke all
    KDC.create("EMPTY.REALM").writeKtab(OneKDC.KTAB);
    try {
      connect();
      throw new Exception("Should not success");
    } catch (GSSException gsse) {
      System.out.println(gsse);
      KrbException ke = (KrbException) gsse.getCause();
      // KrbApReq.authenticate(*) if (dkey == null)...
      // This should have been Krb5.KRB_AP_ERR_NOKEY
      if (ke.returnCode() != Krb5.API_INVALID_ARG) {
        throw new Exception("Not expected failure code: " + ke.returnCode());
      }
    }
  }
  public int initSecContext(InputStream inStream, OutputStream outStream) throws GSSException {

    if (mechCtxt != null && currentState != IN_PROGRESS) {
      throw new GSSExceptionImpl(GSSException.FAILURE, "Illegal call to initSecContext");
    }

    GSSHeader gssHeader = null;
    int inTokenLen = -1;
    GSSCredentialSpi credElement = null;
    boolean firstToken = false;

    try {
      if (mechCtxt == null) {
        if (myCred != null) {
          try {
            credElement = myCred.getElement(mechOid, true);
          } catch (GSSException ge) {
            if (GSSUtil.isSpNegoMech(mechOid) && ge.getMajor() == GSSException.NO_CRED) {
              credElement = myCred.getElement(myCred.getMechs()[0], true);
            } else {
              throw ge;
            }
          }
        }
        GSSNameSpi nameElement = targName.getElement(mechOid);
        mechCtxt = gssManager.getMechanismContext(nameElement, credElement, reqLifetime, mechOid);
        mechCtxt.requestConf(reqConfState);
        mechCtxt.requestInteg(reqIntegState);
        mechCtxt.requestCredDeleg(reqCredDelegState);
        mechCtxt.requestMutualAuth(reqMutualAuthState);
        mechCtxt.requestReplayDet(reqReplayDetState);
        mechCtxt.requestSequenceDet(reqSequenceDetState);
        mechCtxt.requestAnonymity(reqAnonState);
        mechCtxt.setChannelBinding(channelBindings);
        mechCtxt.requestDelegPolicy(reqDelegPolicyState);

        objId = new ObjectIdentifier(mechOid.toString());

        currentState = IN_PROGRESS;
        firstToken = true;
      } else {
        if (mechCtxt.getProvider().getName().equals("SunNativeGSS")
            || GSSUtil.isSpNegoMech(mechOid)) {
          // do not parse GSS header for native provider or SPNEGO
          // mech
        } else {
          // parse GSS header
          gssHeader = new GSSHeader(inStream);
          if (!gssHeader.getOid().equals((Object) objId))
            throw new GSSExceptionImpl(
                GSSException.DEFECTIVE_TOKEN,
                "Mechanism not equal to " + mechOid.toString() + " in initSecContext token");
          inTokenLen = gssHeader.getMechTokenLength();
        }
      }

      byte[] obuf = mechCtxt.initSecContext(inStream, inTokenLen);

      int retVal = 0;

      if (obuf != null) {
        retVal = obuf.length;
        if (mechCtxt.getProvider().getName().equals("SunNativeGSS")
            || (!firstToken && GSSUtil.isSpNegoMech(mechOid))) {
          // do not add GSS header for native provider or SPNEGO
          // except for the first SPNEGO token
        } else {
          // add GSS header
          gssHeader = new GSSHeader(objId, obuf.length);
          retVal += gssHeader.encode(outStream);
        }
        outStream.write(obuf);
      }

      if (mechCtxt.isEstablished()) currentState = READY;

      return retVal;

    } catch (IOException e) {
      throw new GSSExceptionImpl(GSSException.DEFECTIVE_TOKEN, e.getMessage());
    }
  }
  /**
   * Processes the authentication request.
   *
   * @param callbacks
   * @param state
   * @return -1 as succeeded; 0 as failed.
   * @exception AuthLoginException upon any failure.
   */
  public int process(Callback[] callbacks, int state) throws AuthLoginException {
    int result = ISAuthConstants.LOGIN_IGNORE;

    if (!getConfigParams()) {
      initWindowsDesktopSSOAuth(options);
    }

    // retrieve the spnego token
    byte[] spnegoToken = getSPNEGOTokenFromHTTPRequest(getHttpServletRequest());
    if (spnegoToken == null) {
      spnegoToken = getSPNEGOTokenFromCallback(callbacks);
    }

    if (spnegoToken == null) {
      debug.message("spnego token is not valid.");
      throw new AuthLoginException(amAuthWindowsDesktopSSO, "token", null);
    }

    if (debug.messageEnabled()) {
      debug.message(
          "SPNEGO token: \n" + DerValue.printByteArray(spnegoToken, 0, spnegoToken.length));
    }
    // parse the spnego token and extract the kerberos mech token from it
    final byte[] kerberosToken = parseToken(spnegoToken);
    if (kerberosToken == null) {
      debug.message("kerberos token is not valid.");
      throw new AuthLoginException(amAuthWindowsDesktopSSO, "token", null);
    }
    if (debug.messageEnabled()) {
      debug.message(
          "Kerberos token retrieved from SPNEGO token: \n"
              + DerValue.printByteArray(kerberosToken, 0, kerberosToken.length));
    }

    // authenticate the user with the kerberos token
    try {
      authenticateToken(kerberosToken);
      debug.message("WindowsDesktopSSO authentication succeeded.");
      result = ISAuthConstants.LOGIN_SUCCEED;
    } catch (PrivilegedActionException pe) {
      Exception e = extractException(pe);
      if (e instanceof GSSException) {
        int major = ((GSSException) e).getMajor();
        if (major == GSSException.CREDENTIALS_EXPIRED) {
          debug.message("Credential expired. Re-establish credential...");
          serviceLogin();
          try {
            authenticateToken(kerberosToken);
            debug.message("Authentication succeeded with new cred.");
            result = ISAuthConstants.LOGIN_SUCCEED;
          } catch (Exception ee) {
            debug.message("Authentication failed with new cred.");
            throw new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, ee);
          }
        } else {
          debug.message("Authentication failed with GSSException.");
          throw new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, e);
        }
      }
    } catch (GSSException e) {
      int major = e.getMajor();
      if (major == GSSException.CREDENTIALS_EXPIRED) {
        debug.message("Credential expired. Re-establish credential...");
        serviceLogin();
        try {
          authenticateToken(kerberosToken);
          debug.message("Authentication succeeded with new cred.");
          result = ISAuthConstants.LOGIN_SUCCEED;
        } catch (Exception ee) {
          debug.message("Authentication failed with new cred.");
          throw new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, ee);
        }
      } else {
        debug.message("Authentication failed with GSSException.");
        throw new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, e);
      }
    } catch (AuthLoginException e) {
      throw e;
    } catch (Exception e) {
      debug.message("Authentication failed with generic exception.");
      throw new AuthLoginException(amAuthWindowsDesktopSSO, "auth", null, e);
    }
    return result;
  }
  public String generateToken(String authServer) throws Throwable {

    try {
      if (this.stripPort) {
        authServer = authServer.substring(0, authServer.indexOf(":"));
      }

      if (log.isDebugEnabled()) {
        log.debug("init " + authServer);
      }
      /* Using the SPNEGO OID is the correct method.
       * Kerberos v5 works for IIS but not JBoss. Unwrapping
       * the initial token when using SPNEGO OID looks like what is
       * described here...
       *
       * http://msdn.microsoft.com/en-us/library/ms995330.aspx
       *
       * Another helpful URL...
       *
       * http://publib.boulder.ibm.com/infocenter/wasinfo/v7r0/index.jsp?topic=/com.ibm.websphere.express.doc/info/exp/ae/tsec_SPNEGO_token.html
       *
       * Unfortunately SPNEGO is JRE >=1.6.
       */

      /** Try SPNEGO by default, fall back to Kerberos later if error */
      negotiationOid = new Oid(SPNEGO_OID);

      boolean tryKerberos = false;
      try {
        GSSManager manager = GSSManager.getInstance();
        GSSName serverName = manager.createName("HTTP/" + authServer, null);
        gssContext =
            manager.createContext(
                serverName.canonicalize(negotiationOid),
                negotiationOid,
                null,
                GSSContext.DEFAULT_LIFETIME);
        gssContext.requestMutualAuth(true);
        gssContext.requestCredDeleg(true);
      } catch (GSSException ex) {
        log.error("generateToken", ex);
        // BAD MECH means we are likely to be using 1.5, fall back to Kerberos MECH.
        // Rethrow any other exception.
        if (ex.getMajor() == GSSException.BAD_MECH) {
          log.debug("GSSException BAD_MECH, retry with Kerberos MECH");
          tryKerberos = true;
        } else {
          throw ex;
        }
      }
      if (tryKerberos) {
        /* Kerberos v5 GSS-API mechanism defined in RFC 1964.*/
        log.debug("Using Kerberos MECH " + KERBEROS_OID);
        negotiationOid = new Oid(KERBEROS_OID);
        GSSManager manager = GSSManager.getInstance();
        GSSName serverName = manager.createName("HTTP/" + authServer, null);
        gssContext =
            manager.createContext(
                serverName.canonicalize(negotiationOid),
                negotiationOid,
                null,
                GSSContext.DEFAULT_LIFETIME);
        gssContext.requestMutualAuth(true);
        gssContext.requestCredDeleg(true);
      }
      if (token == null) {
        token = new byte[0];
      }
      token = gssContext.initSecContext(token, 0, token.length);
      if (token == null) {
        throw new Exception("GSS security context initialization failed");
      }

      /*
       * IIS accepts Kerberos and SPNEGO tokens. Some other servers Jboss, Glassfish?
       * seem to only accept SPNEGO. Below wraps Kerberos into SPNEGO token.
       */
      if (spengoGenerator != null && negotiationOid.toString().equals(KERBEROS_OID)) {
        token = spengoGenerator.generateSpnegoDERObject(token);
      }

      String tokenstr = new String(Base64.encode(token));
      if (log.isDebugEnabled()) {
        log.debug("Sending response '" + tokenstr + "' back to the auth server");
      }
      return "Negotiate " + tokenstr;
    } catch (GSSException gsse) {
      log.error("generateToken", gsse);
      if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL
          || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED)
        throw new Exception(gsse.getMessage(), gsse);
      if (gsse.getMajor() == GSSException.NO_CRED) throw new Exception(gsse.getMessage(), gsse);
      if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN
          || gsse.getMajor() == GSSException.DUPLICATE_TOKEN
          || gsse.getMajor() == GSSException.OLD_TOKEN)
        throw new Exception(gsse.getMessage(), gsse);
      // other error
      throw new Exception(gsse.getMessage());
    } catch (IOException ex) {
      throw new Exception(ex.getMessage());
    }
  }
Exemple #10
0
  @Override
  public Header authenticate(
      final Credentials credentials, final HttpRequest request, final HttpContext context)
      throws AuthenticationException {
    Args.notNull(request, "HTTP request");
    switch (state) {
      case UNINITIATED:
        throw new AuthenticationException(
            getSchemeName() + " authentication has not been initiated");
      case FAILED:
        throw new AuthenticationException(getSchemeName() + " authentication has failed");
      case CHALLENGE_RECEIVED:
        try {
          final HttpRoute route = (HttpRoute) context.getAttribute(HttpClientContext.HTTP_ROUTE);
          if (route == null) {
            throw new AuthenticationException("Connection route is not available");
          }
          HttpHost host;
          if (isProxy()) {
            host = route.getProxyHost();
            if (host == null) {
              host = route.getTargetHost();
            }
          } else {
            host = route.getTargetHost();
          }
          final String authServer;
          if (!this.stripPort && host.getPort() > 0) {
            authServer = host.toHostString();
          } else {
            authServer = host.getHostName();
          }

          if (log.isDebugEnabled()) {
            log.debug("init " + authServer);
          }
          token = generateToken(token, authServer);
          state = State.TOKEN_GENERATED;
        } catch (final GSSException gsse) {
          state = State.FAILED;
          if (gsse.getMajor() == GSSException.DEFECTIVE_CREDENTIAL
              || gsse.getMajor() == GSSException.CREDENTIALS_EXPIRED) {
            throw new InvalidCredentialsException(gsse.getMessage(), gsse);
          }
          if (gsse.getMajor() == GSSException.NO_CRED) {
            throw new InvalidCredentialsException(gsse.getMessage(), gsse);
          }
          if (gsse.getMajor() == GSSException.DEFECTIVE_TOKEN
              || gsse.getMajor() == GSSException.DUPLICATE_TOKEN
              || gsse.getMajor() == GSSException.OLD_TOKEN) {
            throw new AuthenticationException(gsse.getMessage(), gsse);
          }
          // other error
          throw new AuthenticationException(gsse.getMessage());
        }
      case TOKEN_GENERATED:
        final String tokenstr = new String(base64codec.encode(token));
        if (log.isDebugEnabled()) {
          log.debug("Sending response '" + tokenstr + "' back to the auth server");
        }
        final CharArrayBuffer buffer = new CharArrayBuffer(32);
        if (isProxy()) {
          buffer.append(AUTH.PROXY_AUTH_RESP);
        } else {
          buffer.append(AUTH.WWW_AUTH_RESP);
        }
        buffer.append(": Negotiate ");
        buffer.append(tokenstr);
        return new BufferedHeader(buffer);
      default:
        throw new IllegalStateException("Illegal state: " + state);
    }
  }