I am using a Java Spring Boot application with Waffle SSO library waffle-spring-security4 2.0.0 and have installed and configured MIT Kerberos. The ticket cache is working fine, and running "kinit" creates Kerberos tickets in the ticket cache (C:\Users\XYZ\krb5cc_XYZ). I have verified this by running "klist".

I have implemented JAAS Keytab authentication, which generates the GSSCredential and GSSContext. However, I am unsure about how to extract the Kerberos KT ticket from JAAS or Waffle SSO and authenticate JSch using the extracted ticket.

The following code generates a Kerberos service ticket and successfully authenticates JSch, enabling me to run a command on the remote machine. However, when I run the code for another user, the previous user's TGT is being used. I have disposed of the GSSCredential and GSSContext, but it does not solve the issue.

JSch itself performs all the GSS Authentication and creates the GSSCredential and GSSContext, but I don't have the control to modify the internal behavior and configuration. Therefore, I am performing JAAS authentication and creating GSSCredential and GSSContext.

What suggestions do you have for what I am missing?

public static ArrayList<String> simpleKerberos(String command)
            throws LoginException, GSSException, JSchException, IOException, PrivilegedActionException {
    SunJaasKerberosClient client = new SunJaasKerberosClient();
    client.setDebug(true); // enable debug logs
    ArrayList<String> results = new ArrayList<String>();

    SecurityContext sec = SecurityContextHolder.getContext();
    Authentication authentication = sec.getAuthentication();
    WindowsPrincipal principal = (WindowsPrincipal) authentication.getPrincipal();
    String userNtId = principal.getName();
    String[] userInfo = userNtId.split("\\\\");
    String username = userInfo[1].toLowerCase() + "@" + userInfo[0] + ".DOMAIN.COM";
    String userId = userInfo[1];
    String keyTabFilePath = "C:\\Users\\" + userId + "\\" + userId + ".keytab";

    String ticketCache = "FILE:C:\\Users\\" + userId + "\\krb5cc_" + userId + "";

    String loginContextName = "Krb5Login";

    System.setProperty("java.security.krb5.conf", "C:\\ProgramData\\MIT\\Kerberos5\\krb5.ini");
    System.setProperty("java.security.auth.login.config", "C:\\ProgramData\\MIT\\Kerberos5\\jaas.config");
    //System.setProperty("sun.security.jgss.native", "true");
    System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
    System.setProperty("sun.security.krb5.debug", "true");
    System.setProperty("java.security.debug", "gssloginconfig,configfile,configparser,logincontext");
    System.setProperty("KRB5CCNAME", ticketCache);
    System.setProperty("javax.security.auth.kerberos.ticket.cache", "FILE:" + ticketCache);
    System.setProperty("KRB5_KTNAME", "C:\\Users\\" + userId + "\\" + userId + ".keytab");
    System.setProperty("user.home", "C:\\Users\\" + userId + "");
    System.setProperty("user.name", userId);
    System.setProperty("principal", username);

    String host = "REMOTE.MACHINE.FQDN.COM";
    String realm = "SUBDOMAIN.DOMAIN.COM";

    Configuration loginConfig = new Configuration() {
        @Override
        public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
            if (name.equals(loginContextName)) {
                Map<String, Object> options = new HashMap<>();
                options.put("useKeyTab", "true");
                options.put("useTicketCache", "true");
                options.put("debug", "true");
                options.put("principal", username);
                options.put("ticketCache", ticketCache);

                // Create a Subject that represents the current user's security context
                Subject subject = new Subject();
                Principal principal = new KerberosPrincipal(username);
                subject.getPrincipals().add(principal);

                options.put("javax.security.auth.subject", subject);
                // options.put("runAs", "authenticated");
                // options.put("doNotPrompt", "true");

                return new AppConfigurationEntry[] {
                        new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule",
                                AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options) };
            }
            return null;
        }
    };

    LoginContext loginContext = new LoginContext(loginContextName, null,
            new KerberosKeytabCallbackHandler(username, keyTabFilePath), loginConfig);
    loginContext.login();

    GSSManager gssManager = GSSManager.getInstance();
    GSSName clientName = gssManager.createName(username, GSSName.NT_USER_NAME);

    GSSCredential credential = null;
    Subject subject = null;
    try {

        subject = loginContext.getSubject();
        credential = Subject.doAs(subject, (PrivilegedExceptionAction<GSSCredential>) () -> {
            return gssManager.createCredential(clientName, GSSCredential.DEFAULT_LIFETIME,
                    new Oid("1.2.840.113554.1.2.2"), GSSCredential.ACCEPT_ONLY); ---Not sure, If I should've used INITIATE_ONLY
        });

    } catch (PrivilegedActionException e) {
        e.printStackTrace();
    }
    
    Set<GSSCredential> gssCredentials = subject.getPrivateCredentials(GSSCredential.class);
    gssCredentials.add(credential);

    if (credential == null) {
        throw new RuntimeException("Could not obtain Kerberos credentials for " + username);
    }

    GSSContext gssContext = gssManager.createContext(clientName, new Oid("1.2.840.113554.1.2.2"), credential,
            GSSContext.DEFAULT_LIFETIME);
    
    gssContext.requestCredDeleg(true);
    gssContext.requestMutualAuth(true);
    gssContext.requestConf(true);
    gssContext.requestInteg(true);
    gssContext.requestReplayDet(true);
    gssContext.requestSequenceDet(true);
    gssContext.requestAnonymity(false);
    gssContext.requestLifetime(GSSContext.DEFAULT_LIFETIME);
    gssContext.setChannelBinding(null);

    JSch jsch = new JSch();
    JSch.setConfig("PreferredAuthentications", "gssapi-with-mic");
    Session session = jsch.getSession(username, host, 22);
    session.setConfig("StrictHostKeyChecking", "no");
    UserInfo user = new KerberosUserInfo(credential, gssContext);
    session.setUserInfo(user);
    session.setConfig("userauth.gssapi-with-mic", "com.jcraft.jsch.UserAuthGSSAPIWithMIC");
    session.connect();
    Channel channel = session.openChannel("exec");
    ((ChannelExec) channel).setCommand(command);

    channel.setInputStream(null);
    ((ChannelExec) channel).setErrStream(System.err);

    InputStream in = channel.getInputStream();
    channel.connect();
    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
    String line = null;
    while ((line = reader.readLine()) != null) {
        results.add(line);
    }
    jsch.removeAllIdentity();
    channel.disconnect();
    session.disconnect();
    loginContext.logout();
    loginConfig.refresh();
    gssContext.dispose();
    credential.dispose();

    return results;
}

Kerberos UserInfo

class KerberosUserInfo implements UserInfo {

    private GSSCredential credential;
    private GSSContext context;
    
    public KerberosUserInfo(GSSCredential credential) {
        this.credential = credential;
    }

    public KerberosUserInfo(GSSCredential credential, GSSContext context) {
        this.credential = credential;
        this.context = context;
    }
    
    @Override
    public String getPassphrase() {
        return null;
    }
     @Override
    public String getPassword() {
        return null;
    }
     @Override
    public boolean promptPassphrase(String message) {
        return false;
    }
     @Override
    public boolean promptPassword(String message) {
        return false;
    }

    public boolean promptYesNo(String message) {
        return true;
    }
     @Override
    public void showMessage(String message) {
        System.out.println(message);
    }

    /*
     * public GSSCredential getGSSCredential() { return credential; }
     */
    public GSSCredential getGSSCredential() {
        return credential;
    }

    public GSSContext getGSSContext() {
        return context;
    }

}

KerberosKeytabCallbackHandler

public class KerberosKeytabCallbackHandler implements CallbackHandler {

    private final String principal;
    private final String keytab;

    public KerberosKeytabCallbackHandler(String principal, String keytab) {
        this.principal = Objects.requireNonNull(principal);
        this.keytab = Objects.requireNonNull(keytab);
    }

    @Override
    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        for (Callback callback : callbacks) {
            if (callback instanceof NameCallback) {
                ((NameCallback) callback).setName(principal);
            } else if (callback instanceof PasswordCallback) {
                ((PasswordCallback) callback).setPassword(keytab.toCharArray());
            } else {
                throw new UnsupportedCallbackException(callback);
            }
        }
    }
}
0

There are 0 best solutions below