Node-ldapjs: objectGUID: Buffer encoding issue

Created on 29 May 2018  ·  13Comments  ·  Source: ldapjs/node-ldapjs

Hey there, seems like the returned objectGUID during a search on the active directory has an encoding issue.

Once returned and converted to a readable UUID, it doesn't matched the real value provided by the AD. Is it a know issue or something wrong on my side?

Search response:

// Excepted
{ objectGUID: 'â\u0018!;œóì@¯Wa8–`ß' }
// 3b2118e2-f39c-40ec-af57-6190389660df

// Returned
{ objectGUID: '�\u0018!;���@�Wa�8�`�' }
// 3b2118fd-fdfd-40fd-fd57-61fd38fd60fd

Temporary workaround:

export const formatEntry = entryObject => {
    const { object, attributes } = entryObject;
    const objectGUIDAttribute = attributes.find(
        attr => attr.type === 'objectGUID'
    );

    return {
        ...object,
        objectGUID: Buffer.from(
            objectGUIDAttribute.buffers[0],
            'hex'
        ).toString('binary')
    };
};

Convert objectGUID to readable UUID:

export const objectGUIDToUUID = objectGUID => {
    const hex = Buffer.from(objectGUID, 'binary').toString('hex');

    const p1 =
        hex.substr(-26, 2) +
        hex.substr(-28, 2) +
        hex.substr(-30, 2) +
        hex.substr(-32, 2);

    const p2 = hex.substr(-22, 2) + hex.substr(-24, 2);
    const p3 = hex.substr(-18, 2) + hex.substr(-20, 2);
    const p4 = hex.substr(-16, 4);
    const p5 = hex.substr(-12, 12);

    return [p1, p2, p3, p4, p5].join('-');
};

Most helpful comment

Could this issue possibly be revisited? The binary data gets converted as a UTF-8 string, which does not translate 1-to-1 with raw binary data, because the leading bits contain meta data. Bytes with a decimal value < 128 will work fine, but the other half will not; which is why some of the values in the GUID look fine but others get changed in unexpected (and apparently unrecoverable) ways.

You can get the bin data from attributes on the response.

Like this:

const binaryGUID = entry.attributes.find(attribute => attribute.type === 'objectGUID').buffers[0]

All 13 comments

All values in the object are converted to strings, so if you want any bin data from AD you have to read it off the attributes. I ran into the same issue when I first started using ldapjs.

It'd be nice to be fixed, but at the same time maybe the initial thought was it works for the majority of the values from AD and it's just a convenience. Anyone who needs bin data may have been expected to manually get it from the attributes.

Good to know then :) Thank you @tastypackets - That said, I'll leave this issue opened.

maybe the initial thought was it works for the majority of the values from AD and it's just a convenience.

I believe that is accurate. I would be willing to consider a PR that allows the user to specify which attributes are binary attributes, but that would also be quite complex as the data could be decoded in different ways. So I'm going to close this issue for now.

Could this issue possibly be revisited? The binary data gets converted as a UTF-8 string, which does not translate 1-to-1 with raw binary data, because the leading bits contain meta data. Bytes with a decimal value < 128 will work fine, but the other half will not; which is why some of the values in the GUID look fine but others get changed in unexpected (and apparently unrecoverable) ways.

Could this issue possibly be revisited? The binary data gets converted as a UTF-8 string, which does not translate 1-to-1 with raw binary data, because the leading bits contain meta data. Bytes with a decimal value < 128 will work fine, but the other half will not; which is why some of the values in the GUID look fine but others get changed in unexpected (and apparently unrecoverable) ways.

You can get the bin data from attributes on the response.

Like this:

const binaryGUID = entry.attributes.find(attribute => attribute.type === 'objectGUID').buffers[0]

If you don't know how to get the GUID in Active Directory, check this tutorial: Finding an Active Directory Group's GUID

1. Ensure Advanced Functionality is Enabled

The Attribute Editor is part of ADUC's advanced functionality. You can see it is enabled by going to the view menu and looking for a check mark next to the Advanced Features item.

Step 01

2. Find the objectGUID

Open the properties dialog of the Active Directory group whose objectGUID you need to find, and navigate to the Attribute Editor tab. In this list, in alphabetical order, you can find the objectGUID value for the group.

Step 02

P.S.: Thanks @Alexis-Bize for your Convert objectGUID to readable UUID function 👏👏👏

I've made a readonly AD proxy using ldapjs and i'm fighting with Buffer encoding issue.

I'm using entry.raw structure (so i can fetch the raw buffer).

The client is Apache Directory Studio.

image

We can see in the screenshot that sending the Buffer "as is" (i.e using entry.raw object) result into a malformed GUID, the client is not able to decode it. Same for objectSid. I _think_ every binary fields are impacted.

Wireshark frames show that HEX content of objectGUID are the same (direct access vs ldapjs proxy) ... wired :(

I've tried to use entry.attributes too (see https://github.com/ldapjs/node-ldapjs/issues/481#issuecomment-608097320), and the problem is the same (the Buffer is the same) :

entry.raw                     
<Buffer f8 61 89 50 ab 8e b8 48 b6 5c 6c 13 7c 1e f7 95>

entry.attributes + find 
<Buffer f8 61 89 50 ab 8e b8 48 b6 5c 6c 13 7c 1e f7 95>

Guys, do you have any idea ? any suggestions ?

Thank you !

Try this: (You could method chain the return to clean things up in here, this is something I wrote over 2yrs ago and I just copy pasted it without cleaning it up / refactoring.)

    /**
     * Locates objectGUID and then formats it
     * @param {object} entry This is an entry returned from ldapjs
     * @returns {string} Formated GUID string
     */
    function resolveGUID(entry){
        if(!Array.isArray(entry.attributes))
            throw new Error('Attributes must be an array');

        const binaryGUID = entry.attributes.find(attribute => attribute.type === 'objectGUID').buffers[0];
        const guidFormat = [
            [3,2,1,0],
            [5,4],
            [7,6],
            [8,9],
            [10,11,12,13,14,15]
        ];

        const guidArray = guidFormat.map( part => {
            const stringPart = part.map(byte => {
                // If less than 16 add a 0 to the end
                const byteString = binaryGUID[byte] < 16 ?
                    `0${binaryGUID[byte].toString(16)}` :
                    binaryGUID[byte].toString(16)

                return byteString
            });
            return `${stringPart.join('')}`;
        });
        return guidArray.join('-');
    }

@tastypackets

Thank you. But i'm not speaking about decoding GUID.

I'm using ldapjs as a ldap proxy :

any ldap browser <=> ldapjs server calling ldapjs client routines <=> AD

Using ldapjs as a ldap server, I send original Buffers (entry.raw) provided by ldapjs as a client to the client (i.e any ldap browser). But in my ldap browser, i can see (see screenshot) that the binary value is different with the one i have when i'm searching into the AD directly (i.e no proxy). And the ldap browser failed to decode objectGUID/Sid, because the binary value is not correct.

I was hoping that sending raw buffer was OK, but it's not. This fail :

any ldap browser <=> ldapjs server sending raw buffer ((entry.raw) fetched by ldapjs client <=> AD

So i'm trying to understand why and how to solve this issue. Sticked here

any ldap browser <=> ldapjs server sending modified buffer fetched by ldapjs client <=> AD

It's not UTF8/16 issue, i tested that. I think it's more related to the size of the buffer and/or a padding issue.

In both wireshark traces, i can retrieve the expected objectGUID value in hex format. But it seem's that some bytes are missing before and after the value in the trace using my ldapjs proxy compared to the trace without it.

To simplify, let's say i i'm using ldapjs as a server, and i want to create an objectGUID myself, which will be successfully decoded by an ldap browser.

tcpdump, with/without ldapjs proxy. Sound's like it's not related to objectGUID only. BER/DER/ASN1 specific ?

ping @UziTech

tcpdump

Missing 84 00 00 00 bytes when ldapjs send the response.

Sounds like maybe this should be a new issue, this was originally about getting the bin data directly off the ldapjs client and it sounds like that is working right for you it's just when ldapjs server is involved being used as a proxy the issue comes up.

I never had a use for the ldapjs server, so I don't know it's api/how it works. I usually requested or setup a read only ldap/ad server to connect apps to.

this is my small transfer-function (you can remove the comment... only for understanding)

export const objectGUIDToUUID = (objectGUID)=>
{
  const hexValue = Buffer.from(objectGUID, 'binary').toString('hex')

  return hexValue.replace(
  //   (   $1:A4   )(   $2:A3   )(   $3:A2   )(   $4:A1   )(   $5:B2   )(   $6:B1   )(   $7:C2   )(   $8:C1   )(   $9:D    )(   $10:F    )
      /([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{4})([0-9a-f]{10})/,
      '$4$3$2$1-$6$5-$8$7-$9-$10',
  )
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

ivanhuay picture ivanhuay  ·  5Comments

moehlone picture moehlone  ·  3Comments

SeanPlusPlus picture SeanPlusPlus  ·  5Comments

sean256 picture sean256  ·  5Comments

phi1ipp picture phi1ipp  ·  4Comments