Get ObjectGUID of Microsoft Active Directory using ldapsearch

The object GUID is a globally unique identifier assigned by Microsoft Active Directory Domain Services when the object instance is created. The object GUID is contained in the objectGUID attribute of the object. A GUID is a 128-bit number guaranteed to be unique in space and time. Object GUID never changes, so if an object is renamed or moved anywhere in the enterprise forest, the object GUID remains the same. Applications that save references to objects in Active Directory Domain Services must use the object GUID to ensure that the object reference will survive a rename of the object. The distinguished name for an object might change, but the object GUID will not.

There are many ways to check an object’s GUID. For example, use the GUI tool like ldp.exe. Or use a powershell script as showed below:

PS C:\Users\TESTJLI> Get-ADuser -Identity jli_test|Select-Object ObjectGUID

ObjectGUID
----------
6f91b499-3500-460f-b0c3-ebec52d16003

If you are trying to use ldapsearch on a Linux box, it’s not that easy then because objectGUID returned by ldapsearch will be base64 encoded.

[joe@testjli ~]# ldapsearch -x -H ldaps://joe.ldap.local -b "DC=JOE,DC=LDAP,DC=LOCAL" -s subtree -D "CN=JOELDAP,DC=JOE,DC=LDAP,DC=LOCAL" -w "TESTJLI" "(cn=JOETEST*)" "objectGUID"|grep "objectGUID::"

objectGUID:: 3E8jAoOk/kqO7Nqbme0qnQ==

In the output above, you can see objectGUID is followed by “::”. According to the LDIF standard, the value data that do not fit within a portable subset of ASCII characters are marked with ‘::’ after the attribute name and encoded into ASCII using base64 encoding.

On some version of Linux, you need to specify “-L” to tell ldapsearch to display the search results in LDIF format so that you can see the string. Or you will get “NOT ASCII” for that attribute.

[joe@testjli ~]# ldapsearch -x -H ldaps://joe.ldap.local -b "DC=JOE,DC=LDAP,DC=LOCAL" -s subtree -D "CN=JOELDAP,DC=JOE,DC=LDAP,DC=LOCAL" -w "TESTJLI" "(cn=JOETEST*)" "objectGUID"|grep "objectGUID::"

objectGUID=NOT ASCII

To get the actual objectGUID value you see on a Windows system, you need to decode this base64 string and use “hexdump” to format it with a fprintf-style format string — see fprintf(3).

[joe@testjli ~]# ldapsearch -x -H ldaps://joe.ldap.local -b "DC=JOE,DC=LDAP,DC=LOCAL" -s subtree -D "CN=JOELDAP,DC=JOE,DC=LDAP,DC=LOCAL" -w "TESTJLI" "(cn=JOETEST*)" "objectGUID"|grep "objectGUID::"|awk '{print $2}'|base64 -d -i|hexdump

0000000 b499 6f91 3500 460f c3b0 eceb d152 0360

Without any format, the output of hexdump “b499 6f91 3500 460f c3b0 eceb d152 0360” is 128 bit which means every 2 digits is a byte. Here we can use a format string ‘1/1 ” %02x”‘ to split each byte with a white space (there is a white space between the first double quote ” and % sign) . “1/1” means “iteration count/byte count”. So hexdump handles the input with the format — zero padded to field width 2 as hexadecimal notation until all input data is processed.

[joe@testjli ~]# ldapsearch -x -H ldaps://joe.ldap.local -b "DC=JOE,DC=LDAP,DC=LOCAL" -s subtree -D "CN=JOELDAP,DC=JOE,DC=LDAP,DC=LOCAL" -w "TESTJLI" "(cn=JOETEST*)" "objectGUID"|grep "objectGUID::"|awk '{print $2}'|base64 -d -i|hexdump -e  '1/1 " %02x"'

 99 b4 91 6f 00 35 0f 46 b0 c3 eb ec 52 d1 60 03

Now the rest thing is to assemble it with the correct order:

[joe@testjli ~]# G=($(ldapsearch -x -H ldaps://joe.ldap.local -b "DC=JOE,DC=LDAP,DC=LOCAL" -s subtree -D "CN=JOELDAP,DC=JOE,DC=LDAP,DC=LOCAL" -w "TESTJLI" "(cn=JOETEST*)" "objectGUID"|grep "objectGUID::"|awk '{print $2}'|base64 -d -i|hexdump -e  '1/1 " %02x"'))
[joe@testjli ~]# OBJECTGUID="${G[3]}${G[2]}${G[1]}${G[0]}-${G[5]}${G[4]}-${G[7]}${G[6]}-${G[8]}${G[9]}-${G[10]}${G[11]}${G[12]}${G[13]}${G[14]}${G[15]}"
[joe@testjli ~]# echo $OBJECTGUID
6f91b499-3500-460f-b0c3-ebec52d16003

Or use awk:

[joe@testjli ~]# echo mbSRbwA1D0aww+vsUtFgAw==|base64 -d -i|hexdump -e  '1/1 " %02x"'|awk '{print $4$3$2$1"-"$6$5"-"$8$7"-"$9$10"-"$11$12$13$14$15$16}'
6f91b499-3500-460f-b0c3-ebec52d16003

[joe@testjli ~]# echo mbSRbwA1D0aww+vsUtFgAw==|base64 -d -i|hexdump -e  '1/1 " %02x"'|awk '{print $4$3$2$1"-"$6$5"-"$8$7"-"$9$10"-"$11$12$13$14$15$16}'|awk '{ print toupper($0) }'
6F91B499-3500-460F-B0C3-EBEC52D16003

How do we know the objectGUID should be assembled in this order? Here is the wiki page which explains it. Basically a GUID is stored as a 16-byte (128-bit) number. Microsoft format differs from the UUID standard in the byte order of the first 3 fields. It is split into four fields, defined as follows:

BitsBytesNameEndianness (Microsoft)Endianness (RFC 4122)
324Data1Native (Little)Big
162Data2Native (Little)Big
162Data3Native (Little)Big
648Data4BigBig
GUID format

This endianness applies only to the way in which a GUID is stored, and not to the way in which it is represented in text. GUIDs and RFC 4122 UUIDs should be identical when displayed textually.

One thing I’ve noticed during my work is if the returned base64 coded ASCII string is decoded and assigned to a variable, then use hexdump on the variable, a “00” byte in the middle is removed and another byte is appended at the end. This will cause the final result not correct. So don’t split decode and hexdump in two steps.

[joe@testjli ~]# echo mbSRbwA1D0aww+vsUtFgAw==
mbSRbwA1D0aww+vsUtFgAw==

[joe@testjli ~]# guid="mbSRbwA1D0aww+vsUtFgAw=="
[joe@testjli ~]# echo ${guid}
mbSRbwA1D0aww+vsUtFgAw==

[joe@testjli ~]# echo mbSRbwA1D0aww+vsUtFgAw==|base64 -d -i|hexdump -e  '1/1 " %02x"'
 99 b4 91 6f 00 35 0f 46 b0 c3 eb ec 52 d1 60 03

[joe@testjli ~]# echo ${guid}|base64 -d -i|hexdump -e  '1/1 " %02x"'
 99 b4 91 6f 00 35 0f 46 b0 c3 eb ec 52 d1 60 03


[joe@testjli ~]# wrong1=$(echo mbSRbwA1D0aww+vsUtFgAw==|base64 -d -i)
[joe@testjli ~]# wrong2=`echo mbSRbwA1D0aww+vsUtFgAw==|base64 -d -i`

[joe@testjli ~]# echo ${wrong1} |hexdump -e  '1/1 " %02x"'
 99 b4 91 6f 35 0f 46 b0 c3 eb ec 52 d1 60 03 0a
[joe@testjli ~]# echo ${wrong2} |hexdump -e  '1/1 " %02x"'
 99 b4 91 6f 35 0f 46 b0 c3 eb ec 52 d1 60 03 0a

So why bother to use ldapsearch when there other tools on Windows can do the same thing easily? The reason is we have an application which integrates with an AD to authenticate users. I need to check about 20000 users for their objectGUID values stored within the application’s database against the actual values returned from the AD.

I just prefer to work on a Linux platform than using powershell to query a Oracle database using Oracle Data Provider for .NET (ODP.NET). Another option is to use Oracle’s DBMS_LDAP PL/SQL package. I’ll give an example to use DBMS_LDAP in another post.

As always, for each task, there are many ways to accomplish it. When choosing a method over another, you need to think about:

  • complexity
  • efficiency
  • mantainence

For a small one like I just mentioned above, picking one with which you are comfortable.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s