Changes in RPC Cause Impersonation to Behave Differently (264316)



The information in this article applies to:

  • Microsoft Windows 2000 Server
  • Microsoft Windows 2000 Advanced Server
  • Microsoft Windows 2000 Professional
  • Microsoft Windows 2000 Datacenter Server

This article was previously published under Q264316

SUMMARY

Changes in Remote Procedure Call (RPC) between Microsoft Windows NT 4.0 and Windows 2000 have changed the way that impersonation works in Windows 2000. This may cause problems with any program that uses impersonation and RPC calls. This includes RPC calls that are called either directly or indirectly through operating system library calls. Windows 2000 performs extra security checks when it implements RPC calls that cause impersonation credentials to be checked.

If you have created a program that uses a User Logon token created in another thread's context running under a different user's logon, you may not be able to make calls that require RPC. In Windows 2000, these RPC calls verify that the impersonated token has the appropriate rights to make the RPC call.

MORE INFORMATION

To have the same functionality as in Windows NT 4.0, you must add security Access Control Lists (ACLs) to the Created User token. Typically, you create the token by using LogonUser and then duplicating the token and returning it to the caller. Adding these ACLs does not make your program vulnerable because you cannot add these permissions if you do not have the rights to do so in the first place. To add the appropriate ACLs to the token, use the following sample code as a guide:
// First obtain the Logon token.

    if(!LogonUser(argv[2],
		argv[1],
		argv[3],
		LOGON32_LOGON_NETWORK,
		LOGON32_PROVIDER_DEFAULT,
		&hToken))
		DisplayLastError("LogonUser");		
		
    // Get the SIDs for the Local System and Domain Admins
	
    AllocateAndInitializeSid(
		        &Auth, 1,
			SECURITY_LOCAL_SYSTEM_RID,
			0, 0, 0, 0, 0, 0, 0,
			&LocalSystemSid );
		
    AllocateAndInitializeSid(
			&Auth, 2,
			SECURITY_BUILTIN_DOMAIN_RID,
			DOMAIN_ALIAS_RID_ADMINS,
			0, 0, 0, 0, 0, 0,
			&AliasAdminsSid );
	
    // Validate that the TokenUser and TokenOwner exist and are valid
	
    if ( OpenProcessToken( hClientProc,
			TOKEN_QUERY,
			&ProcessToken ) )
    {
        ProcessUser = (PTOKEN_USER) UserBuffer ;
			
        Status = GetTokenInformation( ProcessToken,
				TokenUser,
				ProcessUser,
				sizeof( UserBuffer ),
				&Size ); 
        if ( NO_ERROR ==  Status)
	{
	    ProcessUser = NULL ;
	}
			
	ProcessOwner = (PTOKEN_OWNER) OwnerBuffer ;
			
	Status = GetTokenInformation( ProcessToken,
				TokenOwner,
				ProcessOwner,
				sizeof( OwnerBuffer ),
				&Size );
			
	if ( NO_ERROR ==   Status ) 
	{
	    ProcessOwner = NULL ;
	}
			
	if ( ProcessOwner && ProcessUser )
	{
	    if ( EqualSid( ProcessOwner->Owner, ProcessUser->User.Sid ) )
	    {
                ProcessOwner = NULL ;
            }
	}
			
	CloseHandle( ProcessToken );
    } else {
	printf("Open token failed with %d\n", GetLastError());
	exit(0);
    }
	
    // 
    // If this is a local system create, 
    // the default object DACL is acceptable. Skip
    // all this. Otherwise, create the ACL
    // 
    // If ProcessUser is null, assume that this is
    // not SYSTEM (because the SYSTEM caller should work)
    // 
		
    if ( (ProcessUser == NULL) || 
                     !EqualSid( ProcessUser->User.Sid, LocalSystemSid ) )
    {
        // Calculate the ACL length based on which tokens will be 
        // added

        AclLength = sizeof( ACL ) + sizeof( ACCESS_ALLOWED_ACE ) + 
                    GetLengthSid( LocalSystemSid ) - sizeof( ULONG ) +
                    sizeof( ACCESS_ALLOWED_ACE ) + 
                    GetLengthSid( AliasAdminsSid ) - sizeof( ULONG ) ;
			
        if ( ProcessOwner )
        {
            AclLength += sizeof( ACCESS_ALLOWED_ACE ) + 
              GetLengthSid( ProcessOwner->Owner ) - sizeof( ULONG ) ;
        }
			
        if ( ProcessUser )
        {
            AclLength += sizeof( ACCESS_ALLOWED_ACE ) + 
	      GetLengthSid( ProcessUser->User.Sid ) - sizeof( ULONG );
        }

        Acl = malloc( AclLength );

        // Now add the various ACEs to the ACL
			
        if ( Acl )
        {
            InitializeAcl( Acl, AclLength, ACL_REVISION2 );
            AddAccessAllowedAce( Acl,
			ACL_REVISION2,
			TOKEN_ALL_ACCESS,
			LocalSystemSid );
				
            AddAccessAllowedAce( Acl,
			ACL_REVISION2,

			TOKEN_READ,
			AliasAdminsSid );
            if ( ProcessOwner )
	    {
		AddAccessAllowedAce( Acl,
				ACL_REVISION2,
				TOKEN_ALL_ACCESS,
				ProcessOwner->Owner );
            }
				
            if ( ProcessUser )
	    {
		AddAccessAllowedAce( Acl,
				ACL_REVISION2,
				TOKEN_ALL_ACCESS,
				ProcessUser->User.Sid );
            }
	}	
    }
			

    // Create the Security attribute for the call to DuplicateHandle		
    SecAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    SecAttr.bInheritHandle = TRUE;
    SecAttr.lpSecurityDescriptor = &SecDesc;
		
    // 
    // Duplicate the handle for impersonation
    // 
		
    if(!DuplicateHandle(
		GetCurrentProcess(),
		hToken,
		(HANDLE)hClientProc, 
		&hDestToken,
		0, // access desired - ignored
		TRUE, // allow inheritance
		DUPLICATE_SAME_ACCESS
		))
	DisplayLastError("DuplicateHandle");
				
You can then pass hDestToken back to the caller.

Modification Type:MajorLast Reviewed:11/21/2003
Keywords:kbinfo kbProgramming kbRPC KB264316