Kerberos delegation: GSSUtil.createSubject returns

2019-08-16 02:39发布

问题:

I am doing kerberos delegation. I noticed that GSSUtil.createSubject(context.getSrcName(), clientCred) returns a Subject without having credentials in it. Prior to that i've done GSSCredential clientCred = context.getDelegCred(); which returns the credentials. Edit: When I hit my service from one machine in same domain, it works, while if accessed from other machine in same domain, it doesn't. Confused what additional settings are needed on AD ? Any help is highly appreciated.

Following is my code:

public class KerberosTest {

    public Subject loginImpl(byte[] kerberosTicket, String propertiesFileName) throws Exception {
        System.setProperty("sun.security.krb5.debug", "true");
//        // no effect // System.setProperty("javax.security.auth.useSubjectCredsOnly","false");

        final Krb5LoginModule krb5LoginModule = new Krb5LoginModule();
        Subject serviceUserSubject = new Subject();
        final Map<String,String> optionMap = new HashMap<String,String>();
        HashMap<String, String> shared = new HashMap<>();

            optionMap.put("keyTab", "C:\\kerberos_files\\sapuser.keytab");
            optionMap.put("principal", "HTTP/SAPTEST@EQSECTEST.LOCAL"); // default realm
//            optionMap.put("principal", "kerberosuser"); // default realm
            optionMap.put("useFirstPass", "true");
            optionMap.put("doNotPrompt", "true");
            optionMap.put("refreshKrb5Config", "true");
            optionMap.put("useTicketCache", "false");
            optionMap.put("renewTGT", "false");
            optionMap.put("useKeyTab", "true");
            optionMap.put("storeKey", "true");
            optionMap.put("isInitiator", "true");
            optionMap.put("useSubjectCredsOnly", "false");

            optionMap.put("debug", "true"); // switch on debug of the Java implementation
            krb5LoginModule.initialize(serviceUserSubject, null, shared, optionMap);

            // login using details mentioned inside keytab
            boolean loginOk = krb5LoginModule.login();
            System.out.println("Login success: " + loginOk);

            // This API adds Kerberos Credentials to the the Subject's private credentials set
            boolean commitOk = krb5LoginModule.commit();

        }

        System.out.println("Principal from subject: " + serviceUserSubject.getPrincipals()); // this must display name of user to which the keytab corresponds to



        Subject clientSubject = getClientContext(serviceUserSubject, kerberosTicket);
        System.out.println("Client Subject-> " + clientSubject);
        System.out.println("Client principal-> "+clientSubject.getPrincipals().toArray()[0]);

        return clientSubject;
    }

    // Completes the security context initialisation and returns the client name.
    private Subject getClientContext(Subject subject, final byte[] kerberosTicket) throws PrivilegedActionException {
        Subject clientSubject = Subject.doAs(subject, new KerberosValidateAction(kerberosTicket));
        return clientSubject;
    }

    private class KerberosValidateAction implements PrivilegedExceptionAction<Subject> {
        byte[] kerberosTicket;

        public KerberosValidateAction(byte[] kerberosTicket) {
            this.kerberosTicket = kerberosTicket;
        }

        @Override
        public Subject run() throws Exception {
            GSSManager gssManager = GSSManager.getInstance();
            GSSContext context = gssManager.createContext((GSSCredential) null);

            Oid kerberosOid = new Oid("1.2.840.113554.1.2.2");

//             context.requestCredDeleg(true); // needed when we are demanding ticket from KDC. In our scenario, we are getting ticket from browser(client)

            // Called by the context acceptor upon receiving a token from the peer. This is our context acceptor
            // This method may return an output token which the application will need to send to the peer for further processing by its initSecContext call.
            // We will only accept the incoming token from Peer (browser) and fwd it to third party system
            while (!context.isEstablished()) {
                byte[] nextToken = context.acceptSecContext(kerberosTicket, 0, kerberosTicket.length);
            }

            boolean established = context.isEstablished();
            String user = context.getSrcName().toString();
            String serviceAccnt = context.getTargName().toString();


            //check if the credentials can be delegated
            if (!context.getCredDelegState()) {
                System.out.println("credentials can not be delegated!");
                return null;
            }

            //get the delegated credentials from the calling peer...
            GSSCredential clientCred = context.getDelegCred();
            //Create a Subject out of the delegated credentials.
            //With this Subject the application server can impersonate the client that sent the request.

            Subject clientSubject = GSSUtil.createSubject(context.getSrcName(), clientCred);
            return clientSubject; // ***this contains only principal name and not credentials !!
        }
    }

回答1:

I figured out one important point, which I didn't find anywhere documented. -

While configuring the kerberos service user in AD, "Trust this user for delegation (Kerberos Only)" will come in effect only for new TGTs which are created after this change is saved.

For example, if the user is logged in on client machine A, say at 10:00 AM.
Administrator enables the delegation to kerberos service user at 10:30 AM.
When the user hits an application URL from browser on machine A, the delegation will not work because his TGT is created before the delegation is turned on (at 10:00 AM).

If user logs out from the machine A and relogins, a new TGT will be generated. Delegation works perfectly for this user now.