connect to AD using anonymous binding and search a user DN - java

I am trying to connect to AD using Anonymous binding and do some operations like search a user DN using CN, find mail id etc ....
Here is the code:
public class TestADAnanymousConnection {
public static void main(String[] args) throws NamingException {
Hashtable<String, Object> env = new Hashtable<String, Object>();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL,
"ldap://localhost:389/dc=myad,dc=com");
env.put(Context.SECURITY_AUTHENTICATION, "none");
DirContext ctx;
ctx = new InitialDirContext(env);
System.out.println(ctx.lookup("cn=Administrator"));
}
}
It shows error message as below:
Exception in thread "main" javax.naming.NamingException: [LDAP: error code 1 - 000004DC: LdapErr: DSID-0C0906E8, comment: **In order to perform this operation a successful bind must be completed on the connection**., data 0, v1db1 remaining name 'cn=Administrator'
at line System.out.println(ctx.lookup("cn=Administrator"));
Can somebody please let me know whether I missed anything in the code?
Thanks.

You cannot. Anonymous binding is only possible for quering root DN. That's it.

Related

Connect to Active Directory using Java JNDI

I am new to using JNDI and I am trying to connect to Active Directory using JNDI and I am facing either Authentication Error or Connection Time out. I am unable to understand what is the potential reason.This how my Active Directory looks like
I have tried the following code
public class ConnectAD {
static DirContext ldapContext;
public static void main(String[] args) throws NamingException {
try {
System.out.println("Début du test Active Directory");
Hashtable<String, String> ldapEnv = new Hashtable<String, String>();
ldapEnv.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
ldapEnv.put(Context.PROVIDER_URL, "ldap://172.16.1.179:389");
ldapEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
ldapEnv.put(Context.SECURITY_PRINCIPAL, "ou=Users,ou=Test1,dc=gigabyte,dc=local");
ldapEnv.put(Context.SECURITY_CREDENTIALS, "5uperCharger");
ldapContext = new InitialDirContext(ldapEnv);
//LdapContext ctx = new InitialLdapContext(ldapEnv,null);
I get the error at while creating the InitialDirContext.
I have an administrator user but I tried giving the cn=administrator but could not connect. I was getting an Authentication Error when I do so.
I also have a name to my ADServer which is GIGA(just trying to provide as much as i can)
Can you please let me know what can be the issue.

LDAP is not delivering results

I currently face the problem that an LDAP-Query is passed to an LDAP-Server and the LDAP-Server does not deliver results.
The query: (&(objectCategory=user)(mail=tester#oop-expert.de))
The given E-Mail is meant to not be found. So an empty result is expected.
In most environment configurations this query will pass perfectly and the LDAP returns an empty result immediately.
I broke down the problem to something that may relate to the network or host from where the query is sent. So the query will be fine if sent from one host/network and from another host/network the LDAP-Server will "starve" my LDAP-Client so the LDAP-Client closes the connection for a client-side timeout.
On the other hand: searching for an email that exists will always lead to an immediate result. Doesn't matter from which host/network.
The LDAP-Server is an Active Directory. There a several domain controllers providing an LDAP-Service, configured "round robin". Access per ip or dns does not make any difference on this subject.
The communication is secured via ssl. (ldaps)
In all situations the connection was established. So authentication and passing the query to the LDAP went fine.
Authorization should not be an issue either. I used the same LDAP user for all situations.
The LDAP client is always a JAVA implementation using InitialContext.
private InitialDirContext createDirContext(String principal, String credentials) throws NamingException {
if (credentials == null || credentials.isEmpty()) {
throw new LDAPLoginException();
}
return new InitialDirContext(createEnvironment(principal, credentials));
}
private Hashtable<String, String> createEnvironment(String principal, String credentials) {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, this.ldapUrl);
// To get rid of the PartialResultException when using Active Directory
env.put(Context.REFERRAL, "follow");
// Needed for the Bind (User Authorized to Query the LDAP server)
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, principal);
env.put(Context.SECURITY_CREDENTIALS, credentials);
return env;
}
The code that builds and executes the query:
private LDAPUser getLDAPUserInfoByUniqueField(String attr, String value) {
DirContext serviceUserContext = null;
NamingEnumeration<SearchResult> results = null;
try {
String searchString = "(&(objectCategory=user)(" + attr + "=" + value + "))";
serviceUserContext = createDirContext(this.serviceUserPrincipal, serviceUserCredentials);
results = serviceUserContext.search("", searchString, createSearchControls()); // blocking...
return createLDAPUserInfo(results);
} catch (LDAPLoginException e) {
throw e;
} catch (Exception e) {
throw new LDAPFatalException(e);
} finally {
try {
if (results != null)
results.close();
} catch (NamingException e) {
}
try {
if (serviceUserContext != null)
serviceUserContext.close();
} catch (NamingException e) {
}
}
}
Utility methods:
private LDAPUser createLDAPUserInfo(NamingEnumeration<SearchResult> results) throws NamingException {
LDAPUser ldapUserInfo = null;
if (results.hasMore()) { // blocking here
SearchResult result = (SearchResult) results.next();
String sAMAccountName = extractsAMAccountName(result);
String distinguishName = extractDistinguishName(result);
String department = extractDepartment(result);
String email = extractEmail(result);
ldapUserInfo = new LDAPUser(sAMAccountName, distinguishName, department, email);
}
return ldapUserInfo;
}
private String extractsAMAccountName(SearchResult result) throws NamingException {
Attributes attrs = result.getAttributes();
Attribute attr = attrs.get("sAMAccountName");
return (String) attr.get();
}
The exception:
de.oopexpert.business.ldap.LDAPFatalException: javax.naming.PartialResultException [Root exception is javax.naming.CommunicationException: oopexpert.de:636 [Root exception is java.net.ConnectException: Connection timed out]]
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByUniqueField(LDAPImpl.java:90)
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByEmail(LDAPImpl.java:57)
Caused by: javax.naming.PartialResultException [Root exception is javax.naming.CommunicationException: oopexpert.de:636 [Root exception is java.net.ConnectException: Connection timed out]]
at com.sun.jndi.ldap.LdapNamingEnumeration.hasMoreImpl(LdapNamingEnumeration.java:242)
at com.sun.jndi.ldap.LdapNamingEnumeration.hasMore(LdapNamingEnumeration.java:189)
at de.oopexpert.business.ldap.impl.LDAPImpl.createLDAPUserInfo(LDAPImpl.java:139)
at de.oopexpert.business.ldap.impl.LDAPImpl.getLDAPUserInfoByUniqueField(LDAPImpl.java:84)
Any hints?
We struggled with four persons what is going on. We figured out, that it has something to do with "name resolution problems" on the server side when "following refferals". The customer specific environment configuration inherently prohibits us to prevent this behaviour. So we came up with a workaround.
As we are operating in an active directory domain we have global catalog servers. The important statement to this is from Microsoft "technet":
The global catalog is a distributed data repository that contains a searchable, partial representation of every object in every domain in a multidomain Active Directory Domain Services (AD DS) forest. The global catalog is stored on domain controllers that have been designated as global catalog servers and is distributed through multimaster replication. Searches that are directed to the global catalog are faster because they do not involve referrals to different domain controllers.
(from What Is the Global Catalog?)
The phrase "do not involve refferals" leads us to my JNDI environment configuration where I set the following:
env.put(Context.REFERRAL, "follow");
So as I did that, the LDAP-Server will ask other LDAP-Servers if the result of a query seems to be incomplete. This was confirmed by our administrators when I started the query and they debugged it on TCP-Level.
Somehow, I maybe do not really represent it correctly, the name of the other LDAP-Server could not be resolved which leads the client to starvation as the first LDAP-Server waits for resolution.
We tried to omit the client parameter "Context.Refferals=follow". Here we get immediate response. But the response wasn't as expected:
javax.naming.PartialResultException: Unprocessed Continuation Reference(s); remaining name ''
This came up because the first LDAP-Server has the opinion to return incomplete data.
Our administrators say that this can never be the case, because "every domain controller" is a "Global Catalog Server".
So my workaround is: I will deal with this exception and interprete it as "no result".

How to connect with Active Directory without a full CN/DN from Java code

I have implemented method:
public static LdapContext buildContext(String username, String password) {
LdapContext context = null;
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, username);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.PROVIDER_URL, DOMAIN_URL);
try {
context = new InitialLdapContext(env, null);
} catch (NamingException e) {
}
return context;
}
I do not know the full CN/DN string. I only pass the name of a user (f.e. Tom) and password.
I have no info about groups etc..
Thank you in advance!
Search for the entry using what information you have. The search result will contain the number of entries that matched the search and the DN of each entry that matched, therefore the search should be as restrictive as possible in order to return just the one entry for which you're looking. Then use that DN to BIND to the server,
What Terry said.
We have an example of performing Basic JNDI Search with Administration Account

Error when trying to do LDAP lookup in active directory

I'm trying to lookup a user on a local active-directory using java.
When I try to execute the code, I get the following error:
Error:
Lookup failed: javax.naming.NamingException: [LDAP: error code 1 -
000004DC: Lda pErr: DSID-0C0906DC, comment: In order to perform this
operation a successful bi nd must be completed on the connection.,
data 0, v1db1 ]; remaining name: 'CN= John Doe, OU=Accounts'
Could anyone tell me what I'm doing wrong?
My code:
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.ldap.LdapContext;
/**
* Demonstrates how to look up an object.
*
* usage: java Lookup
*/
class Lookup {
public static void main(String[] args) {
// Set up the environment for creating the initial context
Hashtable env = new Hashtable(11);
env.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL,
"ldap://localhost:389/DC=PORTAL-UAT,DC=COMPANY,DC=COM");
try {
// Create the initial context
Context ctx = new InitialContext(env);
// Perform lookup and cast to target type
LdapContext b = (LdapContext) ctx
.lookup("CN=John Doe,OU=Accounts");
System.out.println(b);
// Close the context
ctx.close();
} catch (NamingException e) {
System.out.println("Lookup failed: " + e);
}
}
}
As the error message states you have to perform bind operation, i.e. login into the AD. Here is the LDAP Authentication tutorial from Oracle.
The coder should use the ldapsearch command line utility to verify that the connection can be
established that the credentials for the bind DN are correct. This low-level approach will
ensure that a connection can be made from the client system to the target directory server. This
is a basic troubleshooting technique.
For more information, see LDAP: Programming Practices

LDAP user password authentication using JNDI

public static void main(String[] args)
{
String INITCTX = "com.sun.jndi.ldap.LdapCtxFactory";
String MY_HOST = "ldap://Localhost:1389";
String MGR_DN = "cn=John,ou=Users,o=IT,dc=QuizPortal";
String MGR_PW = "password";
//Identify service provider to use
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, INITCTX);
env.put(Context.PROVIDER_URL, MY_HOST);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, MGR_DN);
env.put(Context.SECURITY_CREDENTIALS, MGR_PW);
try
{
// Create the initial directory context
InitialDirContext initialContext = new InitialDirContext(env);
System.out.println("Context Sucessfully Initialized");
}
catch(Exception e)
{
System.err.println(e);
}
}
I would like to ask when I set the MGR_DN = "cn=John,ou=Users,o=IT,dc=QuizPortal" to MGR_DN = "uid=103,ou=Users,o=IT,dc=QuizPortal". Basically changing from cn to uid, I would encounter an error
javax.naming.AuthenticationException: [LDAP: error code 49 - Invalid Credentials]
I am authenticated when is specified as cn=John but not uid=103. Am I not allowed to specify by uid?
If you don't know the exact DN in advance, you should do a search in the LDAP directory first. This can be done more or less like this (make sure you catch the relevant exceptions):
Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL, ldapServerUrl);
env.put(Context.SECURITY_AUTHENTICATION, "none");
SearchControls searchCtrls = new SearchControls();
searchCtrls.setReturningAttributes(new String[] {});
searchCtrls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String filter = "(&(cn=" + identifier + "))";
DirContext ctx = null;
ctx = new InitialDirContext(env);
NamingEnumeration<SearchResult> answer = ctx.search(
ldapBaseDN, filter, searchCtrls);
String fullDN = null;
if (answer.hasMore()) {
fullDN = answer.next().getNameInNamespace();
ctx.close();
ctx = null;
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, fullDN);
env.put(Context.SECURITY_CREDENTIALS, password);
ctx = new InitialDirContext(env);
return true;
}
// Exception otherwise ...
Here, the search filter is "(&(cn=" + identifier + "))" (so, for example (&(cn=John))), but you could use the uid instead. Uniqueness of the results depends on the configuration of the LDAP server. The base DN also depends on the way it's set up (it could be ou=Users,o=IT,dc=QuizPortal in your example).
You have to specify the DN or distinguished name. That's the name the user is bound as in the directory. You can't just select any chain of attributes. If your users are bound via the 'cn' attribute then only the 'cn' attribute is part of the DN.
It looks like a server configuration issue. Here's a similar problem including a solution. Basically you'll have to specify whether to use uid or cn for authentication in ldap-authentication.properties.

Categories