This discussion is archived
1 Reply Latest reply: Sep 16, 2010 6:15 PM by 843810 RSS

Kerberos authentication proxy without KDC connection?

843810 Newbie
Currently Being Moderated
Hello,

I have the task of extending an authentication proxy (well, basically an authentication daemon that works together with a reverse proxy) which supports SSO for SPNEGO-enabled clients. I admit that I currently have a somewhat limited Knowledge of Kerberos in general, and unfortunately of the related Java APIs as well. Nevertheless, I have a problem to solve, so I hope someone may give me some useful hint.

Today, the authentication daemon first performs a login itself (using the JAAS APIs) at the KDC, once, at startup, to receive a TGT, from which it extracts a Subject.
    private Subject login() throws LoginException {
        CallbackHandler callbackHandler = new TextCallbackHandler();
        String name = "com.sun.security.jgss.krb5.initiate";
        LoginContext context = new LoginContext(name, callbackHandler);
        context.login();
        return context.getSubject();
    }
Later, when browser clients need to be authenticated and send their service tickets they got from the KDC, it uses that Subject to perform a dummy privileged operation, which apparently serves as a simple means to both check the validity of the clients service ticket, and extract the user ID from it:
        // First run dummy privileged action
        try {
            Subject.doAs(subject, new DummyPrivilegedAction());
            return true;
        } catch (PrivilegedActionException e) {
          ...
        }

     // Then extract user information (in CheckTokenAction)
     byte[] inToken = Base64.decode(...SPNEGO header data....);
     CheckTokenAction checkTokenAction = new CheckTokenAction(inToken);
     byte[] outToken = Subject.doAs(backEnd.getSubject(), checkTokenAction);
     String username = checkTokenAction.username;

    /**
     * This dummy action helps to find out if a subject is privileged.
     */
    private class DummyPrivilegedAction implements PrivilegedExceptionAction<Object> {
        public Object run() throws GSSException {
            GSSManager manager = GSSManager.getInstance();
            GSSCredential slsOwnCreds = manager.createCredential(null, GSSCredential.DEFAULT_LIFETIME, SPNEGO_MECH,
                    GSSCredential.ACCEPT_ONLY);
            manager.createContext(slsOwnCreds);
            return null;
        }
    }

    /**
     * This class performs the check of a client's token. It's done in an inner class because of
     * java's GSS implementation. It states to do the security relevant actions like checking the
     * client's token only in an implementation of PrivilegedExceptionAction.
     */
    private class CheckTokenAction implements PrivilegedExceptionAction<byte[]> {

        /** The token from the client. */
        private byte[] token;

        /** the error code if authentication failed */
        private int errorCode;

        /** the error message if authentication failed */
        private String errorMessage;

        /** the minor error message with more detail if authentication failed */
        private String minorMessage;

        /** interesting attributes of ticket, for logging purposes */
        private String ticketAttributes = "";

        /** the client's name */
        private String username;

        /** the client's realm */
        private String realm;

        /**
         * Fill the token in constructor, because run() can't take arguments
         *
         * @param token  The token from the client.
         */
        public CheckTokenAction(byte[] token) {
            this.token = token;
        }

        /**
         * Check the spnego token from client and returns the answer token.
         *
         * General spnego could have an arbitrary number of rounds, but we saw that kerberos on
         * windows always needed only one round. However, in a multi-round protocol run still
         * returns the correct answer token, but can't set all attributes before
         * context.isEstablished()==true.
         *
         * RFC:4559 http://tools.ietf.org/html/rfc4559
         *
         * To be able to handle multi-round protocols with a undefined number of message (token)
         * exchanges, the SLS state-model needs an update.
         *
         */
        public byte[] run() throws Exception {
            // prepare new kerberos context for this connection
            GSSManager manager = GSSManager.getInstance();
            GSSCredential slsOwnCreds = manager.createCredential(null, GSSCredential.DEFAULT_LIFETIME, SPNEGO_MECH,
                    GSSCredential.ACCEPT_ONLY);
            GSSContext context = manager.createContext(slsOwnCreds);

            byte[] outToken = null;

            // verify the client's token
            try {
                outToken = context.acceptSecContext(token, 0, token.length);
            } catch (GSSException e) {
                this.errorCode = e.getMajor();
                this.errorMessage = e.getMajorString();
                this.minorMessage = e.getMinorString();
                throw e;
            }
            // fullUsername contains the windows login name in the form
            // user@REALM.COM
            String[] fullUsername = context.getSrcName().toString().split("@");
            username = fullUsername[0];
            realm = fullUsername[1];

            // return the answer token
            return outToken;
        }

        public int getErrorCode() {
            return errorCode;
        }

        public String getErrorMessage() {
            return errorMessage;
        }

        public String getDetailedErrorMessage() {
            return minorMessage;
        }
    }
As I said, I unfortunately do not understand the logic behind this implementation (and cannot ask the programmer who did it). Why would the authentication daemon use a TGT with its own subject to validate service tickets from the clients? Could this be related to the session key in the TGT?

The problem of a customer of ours is, that he has a setup where the authentication daemon has not network connection to the KDC, so the JAAS-login at startup should be eliminated. But I don't see how right now.

Any help (including pointers to enlightening web pages) appreciated. I have started reading into GSS / JAAS tutorials, but they are all concentrating on different usage of Kerberos authentication (Java clients who authenticate themselves, or applications processing service tickets etc.).
  • 1. Re: Kerberos authentication proxy without KDC connection?
    843810 Newbie
    Currently Being Moderated
    In Kerberos, client first requests a TGT and decrypt it with his own password (to get a session key for TGT). After that, when trying to access a service, it requests a service ticket (from the KDC again) and decrypt it with the TGT session key (to get a session key for this service). Finally, he will be able to communicate with the service using this service session key.

    The problem for your case is that both TGT and the service ticket are retrieved internally by the JDK, once during the JAAS login, once during the initSecContext() call, and you cannot control the process.

    I guess you can change the krb5.conf setting on your client to point the KDC to the server, and start a service on the server to act as a proxy to transfer KDC request/response on behalf of the client and KDC. Hope it works.