Giovanni Mascellani
Giovanni Mascellani

Reputation: 1278

Python code to convert from objectSid to SID representation

I want to retrieve base64 encoded objectSid from an LDAP query to an Active Directory database and convert them to the standard SID representation. Can you please give me a Python snippet that does that?

Upvotes: 7

Views: 7442

Answers (5)

David Mulder
David Mulder

Reputation: 8027

If you're using Linux and have Samba installed:

from samba.dcerpc import security
from samba.ndr import ndr_unpack

def convert(binary_sid):
    return str(ndr_unpack(security.dom_sid, binary_sid))

Where binary_sid is the binary representation of the sid.

Upvotes: 3

madjardi
madjardi

Reputation: 5949

use or see implementation in ldap3 ldap-doc
source

ldap3.protocol.formatters.formatters.format_sid

Upvotes: 8

Max
Max

Reputation: 615

A SID in format S-1-5-21-2562418665-3218585558-1813906818-1576 has the following hex raw format: 010500000000000515000000e967bb98d6b7d7bf82051e6c28060000 and can be break down as follows:

  • S : it means simply this is an objectSid
  • 01 : (1) is the revision ans is always 1 as far as I know
  • 05 : count of sub authorities or you can simply say dash count minus two
  • 000000000005 : (5) big endian
  • 15000000 : (21) small endian
  • e967bb98 : (2562418665) small endian
  • d6b7d7bf : (3218585558) small endian
  • 82051e6c : (1813906818) small endian
  • 28060000 : (1576) small endian

Small endian should be read in reverse order. That is how the binary data is represented, where is the least significant byte is stored first. Accordingly Big endian is the order where the most significant byte is stored first. Here is a good article explaining the byte order.

This is a brief introduction about the structure of the objecSid, and this is a nice blog post.

According to these information let's try to read a SID, which is returned as a binary from the LDAP query. The binascii library can be used for Python 2 as well as for Python 3:

from binascii import b2a_hex


def sid_to_str(sid):

    try:
        # Python 3
        if str is not bytes:
            # revision
            revision = int(sid[0])
            # count of sub authorities
            sub_authorities = int(sid[1])
            # big endian
            identifier_authority = int.from_bytes(sid[2:8], byteorder='big')
            # If true then it is represented in hex
            if identifier_authority >= 2 ** 32:
                identifier_authority = hex(identifier_authority)

            # loop over the count of small endians
            sub_authority = '-' + '-'.join([str(int.from_bytes(sid[8 + (i * 4): 12 + (i * 4)], byteorder='little')) for i in range(sub_authorities)])
        # Python 2
        else:
            revision = int(b2a_hex(sid[0]))
            sub_authorities = int(b2a_hex(sid[1]))
            identifier_authority = int(b2a_hex(sid[2:8]), 16)
            if identifier_authority >= 2 ** 32:
                identifier_authority = hex(identifier_authority)

            sub_authority = '-' + '-'.join([str(int(b2a_hex(sid[11 + (i * 4): 7 + (i * 4): -1]), 16)) for i in range(sub_authorities)])
        objectSid = 'S-' + str(revision) + '-' + str(identifier_authority) + sub_authority

        return objectSid
    except Exception:
        pass

    return sid


sid = b'\x01\x05\x00\x00\x00\x00\x00\x05\x15\x00\x00\x00\xe9\x67\xbb\x98\xd6\xb7\xd7\xbf\x82\x05\x1e\x6c\x28\x06\x00\x00'

print(sid_to_str(sid))  # S-1-5-21-2562418665-3218585558-1813906818-1576

Upvotes: 5

Diego Queiroz
Diego Queiroz

Reputation: 3411

This is @Giovanni Mascellani answer, adapted for Python 3.x:

import struct

def convert(binary):
    version = struct.unpack('B', binary[0:1])[0]
    # I do not know how to treat version != 1 (it does not exist yet)
    assert version == 1, version
    length = struct.unpack('B', binary[1:2])[0]
    authority = struct.unpack(b'>Q', b'\x00\x00' + binary[2:8])[0]
    string = 'S-%d-%d' % (version, authority)
    binary = binary[8:]
    assert len(binary) == 4 * length
    for i in range(length):
        value = struct.unpack('<L', binary[4*i:4*(i+1)])[0]
        string += '-%d' % value
    return string

Upvotes: 9

Giovanni Mascellani
Giovanni Mascellani

Reputation: 1278

This should do the trick:

import struct

def convert(binary):
    version = struct.unpack('B', binary[0])[0]
    # I do not know how to treat version != 1 (it does not exist yet)
    assert version == 1, version
    length = struct.unpack('B', binary[1])[0]
    authority = struct.unpack('>Q', '\x00\x00' + binary[2:8])[0]
    string = 'S-%d-%d' % (version, authority)
    binary = binary[8:]
    assert len(binary) == 4 * length
    for i in xrange(length):
        value = struct.unpack('<L', binary[4*i:4*(i+1)])[0]
        string += '-%d' % value
    return string

References: http://blogs.msdn.com/b/oldnewthing/archive/2004/03/15/89753.aspx and http://codeimpossible.com/2008/04/07/Converting-a-Security-Identifier-from-binary-to-string/.

Upvotes: 12

Related Questions