Skip to content

Instantly share code, notes, and snippets.

@rmbolger
Last active April 12, 2024 00:59
Show Gist options
  • Save rmbolger/bc24d4fb3e0a729c322a56f0ec621eb8 to your computer and use it in GitHub Desktop.
Save rmbolger/bc24d4fb3e0a729c322a56f0ec621eb8 to your computer and use it in GitHub Desktop.
Functions for converting a SID to a UID for use with RFC2307 attributes in Active Directory
function Get-UidFromSid
{
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,Position=0,ValueFromPipeline,ValueFromPipelineByPropertyName)]
[System.Security.Principal.SecurityIdentifier]$sid
)
Process {
# convert sid to byte array
$sidBytes = New-Object byte[] $sid.BinaryLength
$sid.GetBinaryForm($sidBytes, 0)
Write-Verbose ("SID bytes: $([System.BitConverter]::ToString($sidBytes))")
# copy the sections we need
$ridDomainBytes = New-Object byte[] 8
[System.Array]::Copy($sidBytes, 16, $ridDomainBytes, 0, 4)
$ridUserBytes = New-Object byte[] 8
[System.Array]::Copy($sidBytes, 24, $ridUserBytes, 0, 4)
Write-Verbose ("Domain portion: $([System.BitConverter]::ToString($ridDomainBytes))")
Write-Verbose ("User portion: $([System.BitConverter]::ToString($ridUserBytes))")
# fix endian'ness if necessary
if (![System.BitConverter]::IsLittleEndian)
{
[System.Array]::Reverse($ridDomainBytes)
[System.Array]::Reverse($ridUserBytes)
}
# convert the byte arrays to longs
$ridDomain = [System.BitConverter]::ToInt64($ridDomainBytes, 0);
$ridUser = [System.BitConverter]::ToInt64($ridUserBytes, 0);
Write-Verbose "Domain(Int64) = $ridDomain, User(Int64) = $ridUser"
# Now we're going to use the first 9 bits of the domain rid followed by the
# first 22 bits of the user rid to make a single 31 bit integer (32-bit signed)
# that will be the new unique UID value for this SID
$ridDomain = ($ridDomain -band 0x1ff) -shl 22
$ridUser = $ridUser -band 0x3fffff
Write-Verbose "Domain(Int64) = $ridDomain, User(Int64) = $ridUser"
return ($ridDomain + $ridUser)
}
<#
.SYNOPSIS
Calculate the UID value for a Windows SID.
.DESCRIPTION
This function duplicates Centrify's algorithm for generating unique UID
values for Active Directory users and groups. The original algorithm was
provided by Centrify and ported to PowerShell by Ryan Bolger.
.PARAMETER sid
The SecurityIdentifier (SID) object to calculate against.
.OUTPUTS
System.Int32 value of the resulting UID.
.EXAMPLE
Get-UidFromSid ([System.Security.Principal.SecurityIdentifier]'S-1-5-21-3040497277-3966670145-4188292625-1137')
Calculate a UID from a fictional SID.
.EXAMPLE
Get-ADUser myuser | Get-UidFromSid
Calculate a UID from an existing Active Directory user via pipeline input.
#>
}
// using System;
// using System.Security.Principal;
public static long GetUidFromSid(SecurityIdentifier sid)
{
//convert to byte array
byte[] sidBytes = new byte[sid.BinaryLength];
sid.GetBinaryForm(sidBytes, 0);
// copy the sections we need
byte[] ridDomainBytes = new byte[8];
Array.Copy(sidBytes, 16, ridDomainBytes, 0, 4);
byte[] ridUserBytes = new byte[8];
Array.Copy(sidBytes, 24, ridUserBytes, 0, 4);
// fix endian'ness if necessary
if (!BitConverter.IsLittleEndian)
{
Array.Reverse(ridDomainBytes);
Array.Reverse(ridUserBytes);
}
// convert the byte arrays to longs
long ridDomain = BitConverter.ToInt64(ridDomainBytes, 0);
long ridUser = BitConverter.ToInt64(ridUserBytes, 0);
// Now we're going to use the first 9 bits of the domain rid followed by the
// first 22 bits of the user rid to make a single 31 bit integer (32-bit signed)
// that will be the new unique UID value for this SID
ridDomain = (ridDomain & 0x1ff) << 22;
ridUser = ridUser & 0x3fffff;
return (ridDomain + ridUser);
}
from __future__ import print_function
import sys
# This script is designed to accept a SID in SDDL format from stdin and output
# a UID value that can be used in the uidNumber or gidNumber attribute on an
# Active Directory domain object. The algorithm was originally provided by
# Centrify and has been ported to Python by Ryan Bolger.
# The code was originally written for Python 2.6.6 on RHEL 6 and has also been
# tested with Python 2.7.6 on Windows.
# All verbose output should be written to stderr and the only output in stdout
# should be the UID/GID value.
# Usage examples:
#
# echo S-1-5-21-3040497277-3966670145-4188292625-1137 | python SIDtoUID.py
# adquery user da06076 --sid | python SIDtoUID.py
#
# The SID in the example above should convert to UID 1346372721
def SIDtoUID(strSid):
# An Active Directory SID in SDDL form for non-built-in users/groups should
# always be in the following format:
# S-1-5-21-XXXXXXXXXX-XXXXXXXXXXX-XXXXXXXXXX-YYYY
# The X's are three 32-bit integers that comprise the 96-bit randomly
# generated domain identifier
# The Y's are a single 32-bit integer that is the object's relative ID in
# the domain (RID)
# For the purpose of this algorithm, we only care about the second section of
# the domain identifier and the user/group RID. So instead of fully parsing
# the complete SDDL into its binary form, we're going to take a short cut and
# just grab those chunks with only some basic sanity checking.
# make sure the beginning looks right
if not strSid.startswith('S-1-5-21-'):
print("ERROR: SID string was invalid.", file=sys.stderr)
exit(1)
# make sure we have the right number of elements
elements = strSid.upper().split("-")
if len(elements) != 8:
print("ERROR: Not enough sub-authority elements found.", file=sys.stderr)
exit(1)
# grab the elements we care about
ridDomain = int(elements[5])
ridUser = int(elements[7])
# Now we're going to use the first 9 bits of the domain rid followed by the
# first 22 bits of the user rid to make a single 31 bit integer (32-bit signed)
# that will be the new unique UID value for this SID
ridDomain = (ridDomain & 0x1ff) << 22
ridUser = ridUser & 0x3fffff
uid = ridDomain + ridUser
return uid
for line in sys.stdin:
print(SIDtoUID(line))
#!/usr/bin/env bash
# This script is designed to accept a SID in SDDL format from stdin and output
# a UID value that can be used in the uidNumber or gidNumber attribute on an
# Active Directory domain object. The algorithm was originally provided by
# Centrify and has been ported to Bash by Ryan Bolger.
# All verbose output should be written to stderr and the only output in stdout
# should be the UID/GID value.
# Usage examples:
#
# echo S-1-5-21-3040497277-3966670145-4188292625-1137 | ./SIDtoUID.sh
# adquery user da06076 --sid | ./SIDtoUID.sh
#
# The SID in the example above should convert to UID 1346372721
set -o nounset -o pipefail
#set -x
SIDtoUID()
{
# make sure the beginning looks right
if [[ $1 != S-1-5-21-* ]]; then
>&2 echo "ERROR: SID string was invalid."
exit 1
fi
# make sure we have the right number of elements
IFS='-' read -ra elements <<< "$1"
if [[ ${#elements[@]} != 8 ]]; then
>&2 echo "ERROR: Not enough sub-authority elements found."
exit 1
fi
# grab the elements we care about
ridDomain=${elements[5]}
ridUser=${elements[7]}
# Now we're going to use the first 9 bits of the domain rid followed by the
# first 22 bits of the user rid to make a single 31 bit integer (32-bit signed)
# that will be the new unique UID value for this SID
let "ridDomain = ($ridDomain & 0x1ff) << 22"
let "ridUser = ($ridUser & 0x3fffff)"
let "uid = $ridDomain + $ridUser"
echo $uid
}
# grab SIDs from file in first argument or stdin
while read sid; do
SIDtoUID $sid
done < "${1:-/dev/stdin}"
@maximd
Copy link

maximd commented Nov 20, 2018

Hi @rmbolger! You mentionned at https://serverfault.com/questions/484908/ad-plugin-or-utility-that-generates-unique-uidnumber-gidnumber-on-creation#comment1111440_748395 (where you posted the powershell version of this script), that you also have bash and python versions of this script. Would it be possible to share them?

@rtomson
Copy link

rtomson commented Jan 25, 2022

I too would love a bash version.

@rmbolger
Copy link
Author

Had to go searching for the old code, but happy to oblige.

@maximd
Copy link

maximd commented Jul 1, 2022

I saw that you updated the gist, thank you very much @rmbolger! May I suggest to edit your answer at https://serverfault.com/a/748395/120699 to add a link to this gist (so that others can learn from it)?

@rmbolger
Copy link
Author

rmbolger commented Jul 6, 2022

Done

@tolid
Copy link

tolid commented Feb 13, 2023

Is there known way in Python or bash for converting a SID to a UID like in SSSD ? The provided code is not consistent with SSSD. For example:
SIDtoUID.sh returns 210303094 for S-1-5-21-527237240-706699826-725345543-587894 and SSSD returns 589787894.

@rmbolger
Copy link
Author

I'm not super familiar with SSSD, but I would think it is either using a different algorithm to generate the UI or it's not generating anything and simply returning the value that was already set in AD for that user.

@AnwarYagoub
Copy link

@rmbolger since the same function can also be used to generate GIDs I modified your code a little bit

function Get-UniqueIDFromSid
{
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true,Position=0,ValueFromPipeline,ValueFromPipelineByPropertyName)]
        [System.Security.Principal.SecurityIdentifier]$sid
    )

    Process {
        # convert sid to byte array
        $sidBytes = New-Object byte[] $sid.BinaryLength
        $sid.GetBinaryForm($sidBytes, 0)
        Write-Verbose ("SID bytes: $([System.BitConverter]::ToString($sidBytes))")

        # copy the sections we need
        $ridDomainBytes = New-Object byte[] 8
        [System.Array]::Copy($sidBytes, 16, $ridDomainBytes, 0, 4)
        $ridUserOrGroupBytes = New-Object byte[] 8
        [System.Array]::Copy($sidBytes, 24, $ridUserOrGroupBytes, 0, 4)
        Write-Verbose ("Domain portion: $([System.BitConverter]::ToString($ridDomainBytes))")
        Write-Verbose ("User/Group portion: $([System.BitConverter]::ToString($ridUserOrGroupBytes))")

        # fix endian'ness if necessary
        if (![System.BitConverter]::IsLittleEndian)
        {
            [System.Array]::Reverse($ridDomainBytes)
            [System.Array]::Reverse($ridUserOrGroupBytes)
        }

        # convert the byte arrays to longs
        $ridDomain = [System.BitConverter]::ToInt64($ridDomainBytes, 0);
        $ridUserOrGroup = [System.BitConverter]::ToInt64($ridUserOrGroupBytes, 0);
        Write-Verbose "Domain(Int64) = $ridDomain, User/Group(Int64) = $ridUserOrGroup"

        # Now we're going to use the first 9 bits of the domain rid followed by the
        # first 22 bits of the user/group rid to make a single 31 bit integer (32-bit signed)
        # that will be the new unique UID/GID value for this SID
        $ridDomain = ($ridDomain -band 0x1ff) -shl 22
        $ridUserOrGroup = $ridUserOrGroup -band 0x3fffff
        Write-Verbose "Domain(Int64) = $ridDomain, User/Group(Int64) = $ridUserOrGroup"

        return ($ridDomain + $ridUserOrGroup)
    }

    <#
    .SYNOPSIS
        Calculate the UID/GID value for a Windows SID.

    .DESCRIPTION
        This function duplicates Centrify's algorithm for generating unique UID/GID
        values for Active Directory users and groups. The original algorithm was
        provided by Centrify and ported to PowerShell by Ryan Bolger.

    .PARAMETER sid
        The SecurityIdentifier (SID) object to calculate against.

    .OUTPUTS
        System.Int32 value of the resulting UID/GID.

    .EXAMPLE
        Get-UniqueIDFromSid ([System.Security.Principal.SecurityIdentifier]'S-1-5-21-3040497277-3966670145-4188292625-1137')

        Calculate a UID/GID from a fictional SID.

    .EXAMPLE
        Get-ADUser myuser | Get-UniqueIDFromSid

        Calculate a UID from an existing Active Directory user via pipeline input.

    .EXAMPLE
        Get-ADGroup mygroup | Get-UniqueIDFromSid

        Calculate a GID from an existing Active Directory group via pipeline input.

    #>
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment