## Groups and Sids

Burton Rosenberg
14 April 1999

### Overview

A SID is a large integer which identifies:

- a global group,
- a local group,
- a computer,
- a domain,
- or a user.

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:

- 1: World Authority
- 2: Local Authority
- 5: NT Authority

Sometimes the first subauthority is zero to represent the canonical element that the sid represents. Examples of such well-known sids are:

- S-1-1-0 World
- S-1-2-0 Local

The NT Authority's first subauthority is important. Some are defined in ntseaphi.h, others we infer from fact:

- S-1-5-1 Dialup
- S-1-5-2 Network
- S-1-5-3 Batch
- S-1-5-4 Interactive
- S-1-5-5 Logon ID
- S-1-5-6 Service
- S-1-5-7 Anonymous Logon (null logon session)
- S-1-5-8 Proxy
- S-1-5-9 Server Logon (Domain Controller account)
- S-1-5-11 Authenticated User
- S-1-5-21 Global group or user, relative to domain sid
- S-1-5-32 Local groups

Some local groups are predefined by NT, for example:

- S-1-5-32-544 Administrators
- S-1-5-32-545 Users
- S-1-5-32-546 Guests
- S-1-5-32-547 Power Users

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) ;

    }