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: | Major | Last Reviewed: | 11/21/2003 |
---|
Keywords: | kbinfo kbProgramming kbRPC KB264316 |
---|
|