Groups and Sids

Burton Rosenberg
14 April 1999

Overview

A SID is a large integer which identifies:

A SID has a canonical printable format:
  printf( "S-%d-%d",
           version_number, 
           top_level_authority ) ;
  while ( subauthority )
    printf ("-%d", subauthority++ ) ;
The version_number is 1. The top_level_authority is: Sometimes the first subauthority is zero to represent the canonical element that the sid represents. Examples of such well-known sids are: The NT Authority's first subauthority is important. Some are defined in ntseaphi.h, others we infer from fact: Some local groups are predefined by NT, for example: The user and global groups are constructed using the prefix S-1-5-21, followed by the domain sid, followed by an additional subauthority identifing the user or group in the domain. The the case of the domain naturally arising from a computer, these sids are equal. The domain or computer sid contains three authorities, that is, is 12 bytes in length.

Users, Local and Global groups follow a containment diagram:

           Local Group
            /    \
    Local User   Global Group in trusted domain
                   \
                 User in same domain
That is, Local groups can contain either global groups from a trusted domain, or users of the local domain. Global Groups can contain users in the same domain as the global group (the relative part of the sids must match). A user token is derived by taking the sids of all containing groups for the user sid in the above containment diagram.

The purpose of all of this is to accumulate permissions for the token. Permissions are attached to sids, with some restrictions as to the type of permissions appropriate to each type of sid. None of this do I understand. The permission of a token is the union of all permissions of its contained sids, containment considered according to the above diagram.

Example

My token contains these sids:

U: S-1-5-21-1179599015-1994013950-622671684-1841
G: S-1-5-21-1179599015-1994013950-622671684-1202
G: S-1-5-21-1179599015-1994013950-622671684-513
G: S-1-5-21-1179599015-1994013950-622671684-1269
G: S-1-5-32-544
G: S-1-5-32-545
G: S-1-5-5-0-8850
G: S-1-5-4
G: S-1-5-11
G: S-1-1-0
G: S-1-2-0
Note that 1179599015-1994013950-622671684 is the develop domain sid. This reads: the user is number 1841 in the develop domain, is a member of groups 1202, 513 and 1269 in this domain, is a member of local groups 544 and 545 (Adminstrators and Users), is assigned logon ID 0-8850, is interactive, is authenticated, is a member of the world and network groups.

Tokentest.c


#include<stdio.h>
#include<string.h>
#include<windows.h>

#include<assert.h>

/*
 * Burton Rosenberg
 * 17 March 1999
 *
 */


PVOID getInfoFromToken( HANDLE hToken, 
		TOKEN_INFORMATION_CLASS tokenInfoClass,
		void ** buffer )
{
	int len ;
	PVOID buf ;
	PVOID p ;

	*buffer = NULL ;
	len = 0 ;
	p = NULL ;

	GetTokenInformation(
			hToken,
			tokenInfoClass,
			NULL,
			0,
			&len ) ;
	if ( (len==0) || !(buf = malloc(len)) ) return NULL ;
	if ( GetTokenInformation(
				hToken,
				tokenInfoClass,
				buf,
				len,
				&len ) ) {
		p = buf ;
	}

	*buffer = buf ;
	return p ;

}

PTOKEN_GROUPS getGroupsFromToken( HANDLE hToken, void ** buffer ) 
{
	return (PTOKEN_GROUPS) getInfoFromToken( 
			hToken, 
			TokenGroups, 
			buffer ) ;
}

PSID getSidFromGroups( PTOKEN_GROUPS pTokenGroups, int index )
{
	if ( index<0 || pTokenGroups->GroupCount<=index )
		return NULL ;
	return pTokenGroups->Groups[index].Sid ;	
}

PSID getSidFromToken ( HANDLE hToken, void ** buffer )
{
	TOKEN_USER * pTokenUser ;
	PSID pSid ;
    pSid = NULL ;

	pTokenUser = (TOKEN_USER *) getInfoFromToken(
			hToken, 
			TokenUser,
			buffer ) ;

	if ( pTokenUser ) {
        pSid = pTokenUser->User.Sid ;
    }

    return pSid ;
}


#define WIDEST_NUMBER 10

int sprint_Sid ( char * buf, int size, PSID pSid )
{
    int subAuthCount, i ;
    int needed_length ;
    char shortBuf[WIDEST_NUMBER+1] ;

    subAuthCount = *GetSidSubAuthorityCount(pSid) ;
    /* room for "S-1-" topauthority ( "-" subauthority ) ** subAuthCount
     *  + NULL
     *
     */
    needed_length = subAuthCount*WIDEST_NUMBER
                       + WIDEST_NUMBER
                    + subAuthCount
                    + 5 ;
    if ( !buf || (needed_length>size) ) {
		if ( buf && size>0 ) buf[0] = '\0' ;
        return needed_length ;
	}

    sprintf( buf, "S-1-%d",
        GetSidIdentifierAuthority(pSid)->Value[5] ) ;

    for ( i=0; i<subAuthCount; i++ ) {
        sprintf( shortBuf, "-%d", *GetSidSubAuthority( pSid, i ) ) ;
        strcat( buf, shortBuf ) ;
    }

    return 0 ;

}


int main(int argc, char * argv[])
{
	HANDLE hToken ;
	PSID pSid ;
	PVOID buf ;
	PTOKEN_GROUPS pGroups ;
	char cs[5000] ;
	int i ;
	
	if ( !OpenProcessToken(
				GetCurrentProcess(),
				TOKEN_ALL_ACCESS,
				&hToken )) {
		printf("did not get token\n") ;
		return 0 ;
	}


	pSid = getSidFromToken( hToken, &buf ) ;
	if ( !pSid ) {
		printf("did not get SID from Token\n") ;
		return 0 ;
	}

	sprint_Sid( cs, sizeof(cs), pSid ) ;
	printf("U: %s\n", cs ) ;
	free(buf) ;

	pGroups = getGroupsFromToken( hToken, &buf ) ;
	if ( !pGroups ) {
		printf("did not get Groups from Token\n") ;
		return 0 ;
	}

	i = 0 ;
	while ( pSid = getSidFromGroups( pGroups, i++ ) ) {
		sprint_Sid( cs, sizeof(cs), pSid ) ;
		printf( "G: %s\n", cs ) ;
	}
	free(buf) ;
	
}