Java JNDI API User cannot authenticate to ADs with multi-forests setup

做~自己de王妃 提交于 2019-12-11 17:48:25

问题


In my testing env, I have setup two active directory forests A and B, each has one domain controller and the forests have two way trust setup.

I have users - userA in forestA and userB in forestB.

I have used adfind.exe to test if both userA and userB can authenticate by hitting the forestA endpoint, like .\AdFind.exe -h -u userA@forestA.com -up .\AdFind.exe -h -u userB@forestA.com -up

Both users can authenticate through forestA as expected, as they have two way trust setup.

However, userB can never authenticate with the Java JNDI APIs. I've been searching all around, the most I could find is that they suggest to use DN or UPN for the Context.SECURITY_PRINCIPAL attribute, and none of them worked for me.

And I'm pretty sure that the principle and credentials are correct as userB can authenticate fine if the host is changed to forestB where the user is stored.

Here is the standard JNDI code that I use -

Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, "ldap://forestA.com");
//env.put(Context.SECURITY_PRINCIPAL, "userB@forestB.com");
//env.put(Context.SECURITY_PRINCIPAL, 
    CN=userB,OU=marketing,DC=forestB,DC=com");
env.put(Context.SECURITY_PRINCIPAL, "forestB\\userB");
env.put(Context.SECURITY_CREDENTIALS, "mypass");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.REFERRAL, "follow");

DirContext dirContext = new InitialDirContext(env);

I've been stuck on this for days and appreciate for any help!


回答1:


After days of researches and digging into this issue, I've finally found a solution; hopefully it will be beneficial to those who have been struggling like me :) ...

First of all, let me correct the adfind example that I used above, I had a typo there and it should be the following -

.\AdFind.exe -h forestA.com -u userA@forestA.com -up pass
.\AdFind.exe -h forestA.com -u userB@forestB.com -up pass

Both adfind commands will pass as expected. However,

.\AdFind.exe -h forestA.com -u userB@forestB.com -up pass -simple

this will fail as it's forced to use 'simple' authentication. And that made me think, adfind by default is using others, like Kerberos authentication and 'simple' is not for cross forest authentication.

I started to change my Java JNDI code from 'simple' to Kerberos(GSSAPI) and that worked!! Make sure your realm name is in captial case and the ldap URL has to be the hostname. I struggled quiet a bit on getting that to work too.

I've included all the sample code and config files, they are all you need to get it to work.

JndiAction.java

public class JndiAction implements java.security.PrivilegedAction {
    private String url;
    private DirContext ctx;

    public Object run() {
        performJndiOperation();
        return null;
    }

    private void performJndiOperation(/*String[] args*/) {
        // Set up environment for creating initial context
        Hashtable env = new Hashtable(11);

        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, "ldap://server01.forestb.com");
        // Request the use of the "GSSAPI" SASL mechanism
        // Authenticate by using already established Kerberos credentials
        env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");

        try {
            /* Create initial context */
            ctx = new InitialDirContext(env);
            System.out.println("Authenticated!");

            // Close the context when we're done
            ctx.close();
        } catch (NamingException e) {
            e.printStackTrace();
        }
    }
}

GssExample.java

public class GssExample {
    public static void main(String[] args) {
        System.setProperty("sun.security.debug", "true");
        System.setProperty("sun.security.krb5.debug", "true");
        System.setProperty("java.security.krb5.conf", "C:\\dev\\active_directory\\krb5.conf");
        System.setProperty("java.security.auth.login.config", "C:\\dev\\active_directory\\jaas.conf");

        // 1. Log in (to Kerberos)
        LoginContext lc = null;
        try {
            SampleCallbackHandler cbHandler = new SampleCallbackHandler();
            cbHandler.setUsername("userb@FORESTB.COM"); 
            cbHandler.setPassword("mypass");
            lc = new LoginContext(GssExample.class.getName(), cbHandler);

            // Attempt authentication
            lc.login();
        } catch (Exception le) {
            le.printStackTrace();
            System.exit(-1);
        }

        // 2. Perform JNDI work as logged in subject
        Subject.doAs(lc.getSubject(), new JndiAction());
    }
}  

SampleCallbackHandler.java

public class SampleCallbackHandler implements CallbackHandler {
    private String username = "";
    private String password = "";

    public void handle(Callback[] callbacks)
        throws java.io.IOException, UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof NameCallback) {
                NameCallback cb = (NameCallback)callbacks[i];
                cb.setName(this.username);

            } else if (callbacks[i] instanceof PasswordCallback) {
                PasswordCallback cb = (PasswordCallback)callbacks[i];
                String pw = this.password; 
                char[] passwd = new char[pw.length()];
                pw.getChars(0, passwd.length, passwd, 0);
                cb.setPassword(passwd);
            } else {
                throw new UnsupportedCallbackException(callbacks[i]);
            }
        }
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

krb5.conf

[libdefaults]
    default_realm = FORESTB.COM
[realms]
    FORESTA.COM  = {
           kdc = foresta.com
    }
    FORESTB.COM  = {
           kdc = forestb.com
    }

jaas.conf

GssExample {
    com.sun.security.auth.module.Krb5LoginModule required
      client=TRUE
      debug=true
      useTicketCache=true
      principal=david;
};


来源:https://stackoverflow.com/questions/51217894/java-jndi-api-user-cannot-authenticate-to-ads-with-multi-forests-setup

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!