11 Replies Latest reply on Mar 13, 2009 12:14 PM by 800479

# JNDI, Active Directory and objectGUID's

For some reason my original post on this topic was dropped from the forum.

Active Directory and for that matter Active Directory Application Mode (ADAM) allow most objects to be renamed or moved. There are internal mechanisms to ensure the integrity of the directory which make objects rename safe.

For example assume there is a user object cn=Jenny Smith ,ou=Finance,dc=antipodes,dc=com and she is a member of the group cn=Tennis Players,ou=Social Club,dc=antipodes,dc=com

If Jenny gets married, and changes jobs, her distinguished name becomes cn=Jenny Smith-Jones,ou=Sales,dc=antipodes,dc=com.

Because AD is rename safe, her group memberships are maintained.

However how does an application keep track of directory objects as they are renamed or moved? For every directory object there is one attribute called objectGUID which is unique and invariant. objectGUID's are created by the system and are guaranteed to be globally unique.

The following sample code demonstrates retrieving the objectGUID attribute and displaying it in both the binary and string form.
/**
* searchforguid.java
* 5 July 2001
* Sample JNDI application to perform a search against the Active Directory
* and also return the objectGUID in both binary and string formats
*
*/

import java.util.Hashtable;
import javax.naming.ldap.*;
import javax.naming.directory.*;
import javax.naming.*;

public class searchforguid     {
public static void main (String[] args)     {

Hashtable env = new Hashtable();
//Can use either DN, NTLM or UPN style credentials
String ldapURL = "ldap://myddc.antipodes.com:389";

env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");

//set security credentials, note using simple cleartext authentication
env.put(Context.SECURITY_AUTHENTICATION,"simple");

//specify attributes to be returned in binary format
env.put("java.naming.ldap.attributes.binary","objectGUID");

//connect to my domain controller
env.put(Context.PROVIDER_URL,ldapURL);
try {

//Create the initial directory context
LdapContext ctx = new InitialLdapContext(env,null);

//Create the search controls
SearchControls searchCtls = new SearchControls();

//Specify the attributes to return
String returnedAtts[]={"sn","givenName","mail","objectGUID"};
searchCtls.setReturningAttributes(returnedAtts);

//Specify the search scope
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);

//specify the LDAP search filter
String searchFilter = "(&(objectClass=user)(cn=Albert Einstein))";

//Specify the Base for the search
String searchBase = "DC=Antipodes,DC=Com";

//initialize counter to total the results
int totalResults = 0;

//Search for objects using the filter
NamingEnumeration answer = ctx.search(searchBase, searchFilter, searchCtls);

//Loop through the search results

totalResults++;

System.out.println(">>>" + sr.getName());

// Print out some of the attributes, catch the exception if the attributes have no values
Attributes attrs = sr.getAttributes();
if (attrs != null) {
try {
System.out.println("   name: " + attrs.get("givenName").get() + " " + attrs.get("sn").get());
System.out.println("   mail: " + attrs.get("mail").get());
byte[] GUID = (byte[])attrs.get("objectGUID").get();
String strGUID = "";
String byteGUID = "";
//Convert the GUID into string using the byte format
for (int c=0;c<GUID.length;c++) {
}
//convert the GUID into string format
strGUID = "{";
strGUID = strGUID + "-";
strGUID = strGUID + "-";
strGUID = strGUID + "-";
strGUID = strGUID + "-";
strGUID = strGUID + "}";
System.out.println("GUID (String format): " + strGUID);
System.out.println("GUID (Byte format): " + byteGUID);

}
catch (NullPointerException e)     {
System.err.println("Problem listing attributes: " + e);
}

}

}

System.out.println("Total results: " + totalResults);
ctx.close();

}
catch (NamingException e) {
System.err.println("Problem searching directory: " + e);
}
}

return (k<0xF)?"0" + Integer.toHexString(k):Integer.toHexString(k);
}

}
Now there are two ways to retrieve an object using its objectGUID.

The first is to simply perform a search using the objectGUID in it's binary format. (Remember that the backslash character is an LDAP special character and must be escaped).

In this case just modify your application to use a search filter that includes the binary form of the objectGUID
//specify the LDAP search filter
//This is the binary format of the objectGUID
//Note that I've escaped the '\' character
String searchFilter = "(objectGUID=\\67\\8a\\44\\7c\\3b\\92\\ee\\48\\b2\\1a\\34\\51\\f2\\f7\\58\\ca)";
To use the string form of the search filter, in older versions of JNDI before they screwed up the LDAP name parsing :-(
all you needed to do was issue a base level search using the objectGUID as the searchbase, and objectClass=* as the search filter.
//Specify the search scope
//Note for GUID=xxxx must use base level search
searchCtls.setSearchScope(SearchControls.OBJECT_SCOPE);

//specify the LDAP search filter
//Note for GUID=xxxx must use objectClass filter
String searchFilter = "(objectClass=*)";

//Specify the Base for the search
//This is the string form of the GUID without the braces
String searchBase = "<GUID=7c448a67-923b-48ee-b21a-3451f2f758ca>";
Unfortunately in current Java releases, this results in a InvalidNamingException because it appears that the NamingEnumeration cannot parse a dn that contains LDAP special characters, in this case the <> characters that enclose the GUID.
Fortunately you can just simply retrieve the object by binding directly to it, as this sample illustrates.
/**
* retrievebyguid.java
* 5 July 2001
* Sample JNDI application to retrieve an Active Directory
* object using the string form of its objectGUID
*/

import java.util.Hashtable;
import javax.naming.ldap.*;
import javax.naming.directory.*;
import javax.naming.*;

public class retrievebyguid     {
public static void main (String[] args)     {

Hashtable env = new Hashtable();
String ldapURL = "ldap://myddc.antipodes.com:389";

env.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.ldap.LdapCtxFactory");

//set security credentials, note using simple cleartext authentication
env.put(Context.SECURITY_AUTHENTICATION,"simple");

//connect to my domain controller
env.put(Context.PROVIDER_URL,ldapURL);
try {

//Create the initial directory context
LdapContext ctx = new InitialLdapContext(env,null);

//Bind directly using the string form of the GUID
String strGUID = "<GUID=7c448a67-923b-48ee-b21a-3451f2f758ca>";

//Specify the attributes to return
String returnedAtts[]={"distinguishedName","sn","givenName","mail"};

Attributes attr = ctx.getAttributes(strGUID,returnedAtts);

//print out the retrieved attributes
System.out.println("DN: " + attr.get("distinguishedName").get());
System.out.println("Name: " + attr.get("givenName").get() + " " + attr.get("sn").get());
System.out.println("Mail: " + attr.get("mail").get());

ctx.close();

}
catch (NamingException e) {
System.err.println("Problem searching directory: " + e);
}
}
}
• ###### 1. Re: JNDI, Active Directory and objectGUID's
Hi Steven,
From the above code, I manage to get the objectGUID and gets it string form and save it to the database. I tried various ways to query it using the examples you had provided to get back to the object
It always gave me an exception.

I had my objectGUID to be returned in the binary form even while comparing once I had that stuff in the String form.

It did not work. Now this string in maintained in the database, is there any way to sort of reverse engineer it and obtain the same binary string as it orginally was, then I could use a searchfilter and obtain that object.

return (k><0xF)?"0" + Integer.toHexString(k):Integer.toHexString(k);

there is no expression in java as k>< did you mean k> or k< or k!= please specify that too...

Thanks
• ###### 2. Re: JNDI, Active Directory and objectGUID's
Looks like either a typo in my code, or something in the web page formatting that screwed things up.

static String AddLeadingZero(int k) {
return (k<0xF)?"0" + Integer.toHexString(k):Integer.toHexString(k);
}
If retrieving an object by the string form of its guid,because the LDAP name parser doesn't like <> characters, you must retrieve the object directly. For example:
LdapContext ctx = new InitialLdapContext(env,null);
String strGUID = "<GUID=7c448a67-923b-48ee-b21a-3451f2f758ca>";
Attributes attr = ctx.getAttributes(strGUID);
If retrieving the GUID using the binary form then you can issue an LDAP search with the binary form of the GUID (remember to escape correctly).
SearchControls searchCtls = new SearchControls();
searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
String returnedAtts[] = {"sn","givenName","mail"};
searchCtls.setReturningAttributes(returnedAtts);
String searchFilter = "(objectGUID=\\67\\8a\\44\\7c\\3b\\92\\ee\\48\\b2\\1a\\34\\51\\f2\\f7\\58\\ca)";
String searchBase = "dc=antipodes,dc=com";
Naming Enumeration answer = ctx.search(searchBase,searchFilter,searchCtls);
• ###### 3. Re: JNDI, Active Directory and objectGUID's
Thats great it finally worked.:). I am attaching a peice of code for those who want to get the Escaped form the string. The input String is of the form

"6448d4ff-813f-432b-a0be-e2504173f0b6" and output will be the escaped string that can be used in the search filters.
i.e \ff\d4\48\64\3f\81\2b\43\a0\be\e2\50\41\73\f0\b6
     public static String objectGUID(String s){
StringBuffer sb = new StringBuffer();
StringTokenizer st = new StringTokenizer(s,"-");
String temp,s1,s2,s3,s4,s5;
temp=s1=s2=s3=s4=s5=null;
int i=1;
while(st.hasMoreTokens()){
temp = st.nextToken();
switch  (i){
case 1 :
s1= temp;
break;
case 2 :
s2= temp;
break;
case 3 :
s3= temp;
break;
case 4 :
s4= temp;
break;
case 5 :
s5= temp;
break;
default :
break;
}
i++;
}

sb.append("\\" + s1.substring(6,8));
sb.append("\\" + s1.substring(4,6));
sb.append("\\" + s1.substring(2,4));
sb.append("\\" + s1.substring(0,2));

sb.append("\\" + s2.substring(2,4));
sb.append("\\" + s2.substring(0,2));

sb.append("\\" + s3.substring(2,4));
sb.append("\\" + s3.substring(0,2));

sb.append("\\" + s4.substring(0,2));
sb.append("\\" + s4.substring(2,4));

sb.append("\\" + s5.substring(0,2));
sb.append("\\" + s5.substring(2,4));
sb.append("\\" + s5.substring(4,6));
sb.append("\\" + s5.substring(6,8));
sb.append("\\" + s5.substring(8,10));
sb.append("\\" + s5.substring(10,12));

return sb.toString();
}
Thanks Steven.
• ###### 4. Re: JNDI, Active Directory and objectGUID's
In your example you call indexes 14 and 15 of the byteArray in order to build out the entires GUID string. When i tried this against my environment, I only have 13 14 items in the byte array so I had to comment out those last lines.

I also had to change how you built the byteArray becuase I was getting class cast exceptions from your example.

Here is how i am building the byteArray.
String guid = (String)attrs.get("objectGUID").get();
byte[] GUID = guid.getBytes();
I had to go this route becuase the .get("objectGUID") calls returns a string in my case, so the initial example of casting it as a byte[] was blowing up.

With this in place, I was able to get a string and byte version of the GUID. When I turn around and try to query by the GUID (either as a String or a Byte version) I get no results. What I am thinking is that I am not converting the GUID into a valid string. Is there a way to check this, or can I send you some more examples and have you look at it?

I feel like I am finally really close here, but am just missing a little detail.
• ###### 5. Re: JNDI, Active Directory and objectGUID's
There must be a problem in your code as the GUID is always a fixed length.
Don't forget to request that the objectGUID attribute is to be returned as binary data rather than a string by specifiying
env.put("java.naming.ldap.attributes.binary","objectGUID");
When performing a search using a string format of the GUID, don't forget to escape the \ characters.
• ###### 6. Re: JNDI, Active Directory and objectGUID's
small bug here:

replace:
return (k<0xF)?"0" + Integer.toHexString(k):Integer.toHexString(k);

with:

return (k <= 0xF)?"0" + Integer.toHexString(k):Integer.toHexString(k);
• ###### 7. Re: JNDI, Active Directory and objectGUID's
failing to specify the attribute type as binary by the following code

env.put("java.naming.ldap.attributes.binary","objectGUID");

would result in java.lang.ClassCastException if you try to typecaste it into a byte[].

To bypass this if you typecast it into String this would corrupt the
the binary content of the objectGUID.

• ###### 8. Re: JNDI, Active Directory and objectGUID's
in steven's code... he converts the byte array to both a string hex format (strGUID) and string byte format (byteGUID).

How do I go about converting the string hex format to the byte format or byte array?

strGUID --> byteGUID
strGUID --> GUID
• ###### 9. Re: JNDI, Active Directory and objectGUID's
never mind....
LdapContext ctx = new InitialLdapContext(env,null);
Attributes attr = ctx.getAttributes(strGUID);