Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

701 lines
20 KiB

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 2000
//
// File: user.cxx
//
// Contents: Context manipulation functions
//
//
// History: KDamour 15Mar00 Derrived from NTLM context.cxx
//
//------------------------------------------------------------------------
#include "global.h"
// This list contains all of the User Contexts
LIST_ENTRY l_UserCtxtList;
// Lock for access to UserCtxtList
RTL_CRITICAL_SECTION l_UserCtxtCritSect;
// Indicate if completed Initialization of Credential Handler
BOOL g_bUserContextInitialized = FALSE;
//+--------------------------------------------------------------------
//
// Function: UserCtxtHandlerInit
//
// Synopsis: Initializes the context manager package
//
// Arguments: none
//
// Returns: NTSTATUS
//
// Notes: Called by SpInstanceInit
//
//---------------------------------------------------------------------
NTSTATUS
UserCtxtHandlerInit(VOID)
{
NTSTATUS Status = STATUS_SUCCESS;
//
// Initialize the Context list to be empty.
//
Status = RtlInitializeCriticalSection(&l_UserCtxtCritSect);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "UserCtxtHandlerInit: Failed to initialize critsec 0x%x\n", Status));
goto CleanUp;
}
InitializeListHead( &l_UserCtxtList );
// Simple variable test to make sure all initialized;
g_bUserContextInitialized = TRUE;
CleanUp:
return Status;
}
// Add a Context into the UserMode Context List
NTSTATUS
UserCtxtHandlerInsertCred(
IN PDIGEST_USERCONTEXT pUserContext
)
{
RtlEnterCriticalSection( &l_UserCtxtCritSect );
DebugLog((DEB_TRACE, "UserCtxtHandlerRelease: (RefCount) linked 0x%x\n", pUserContext->LsaContext));
InsertHeadList( &l_UserCtxtList, &pUserContext->Next );
RtlLeaveCriticalSection( &l_UserCtxtCritSect );
return STATUS_SUCCESS;
}
// Initialize a UserMode Context
NTSTATUS NTAPI
UserCtxtInit(
PDIGEST_USERCONTEXT pContext
)
{
NTSTATUS Status = STATUS_SUCCESS;
DebugLog((DEB_TRACE_FUNC, "UserCtxtInit: Entering\n"));
ASSERT(pContext);
if (!pContext)
{
return STATUS_INVALID_PARAMETER;
}
ZeroMemory(pContext, sizeof(DIGEST_USERCONTEXT));
DebugLog((DEB_TRACE_FUNC, "UserCtxtInit: Leaving \n"));
return Status;
}
// Once done with a context - release the resouces
NTSTATUS NTAPI
UserCtxtFree(
IN PDIGEST_USERCONTEXT pContext
)
{
NTSTATUS Status = STATUS_SUCCESS;
int i = 0;
DebugLog((DEB_TRACE_FUNC, "UserCtxtFree: Entering with LSA context 0x%x\n", pContext->LsaContext));
ASSERT(pContext);
ASSERT(pContext->lReferences == 0);
ASSERT(pContext->lReferenceHandles == 0);
if (!pContext)
{
return STATUS_INVALID_PARAMETER;
}
if (pContext->ClientTokenHandle)
{
NTSTATUS IgnoreStatus;
IgnoreStatus = NtClose(pContext->ClientTokenHandle);
// ASSERT (NT_SUCCESS (IgnoreStatus));
if (!NT_SUCCESS(IgnoreStatus))
{
DebugLog((DEB_ERROR, "UserCtxtFree: Could not Close the TokenHandle!!!!\n"));
}
pContext->ClientTokenHandle = NULL;
}
if (pContext->hSealCryptKey)
{
CryptDestroyKey( pContext->hSealCryptKey );
pContext->hSealCryptKey = NULL;
}
if (pContext->hUnsealCryptKey)
{
CryptDestroyKey( pContext->hUnsealCryptKey );
pContext->hUnsealCryptKey = NULL;
}
StringFree(&(pContext->strSessionKey));
UnicodeStringFree(&(pContext->ustrAccountName));
//
// Values utilized in the Initial Digest Auth ChallResponse
// Can be utilized for defaults in future MakeSignature/VerifySignature
//
for (i=0; i < MD5_AUTH_LAST; i++)
{
StringFree(&(pContext->strParam[i]));
}
DigestFreeMemory(pContext);
DebugLog((DEB_TRACE_FUNC, "UserCtxtFree: Leaving\n"));
return Status;
}
/*++
Routine Description:
This routine checks to see if the Context is for the specified
Client Connection, and references the Context if it is valid.
The caller may optionally request that the Context be
removed from the list of valid Contexts - preventing future
requests from finding this Context.
Arguments:
ContextHandle - Points to the ContextHandle of the Context
to be referenced.
DerefContext - This boolean value indicates whether the caller
wants the Context to be removed from the list
of Contexts. TRUE indicates the Context is to be removed.
FALSE indicates the Context is not to be removed.
Return Value:
NULL - the Context was not found.
Otherwise - returns a pointer to the referenced Context.
--*/
NTSTATUS NTAPI
UserCtxtHandlerHandleToContext(
IN ULONG_PTR ContextHandle,
IN BOOLEAN fDerefContextHandle,
IN BOOLEAN fRefContextHandle,
OUT PDIGEST_USERCONTEXT *ppContext
)
{
NTSTATUS Status = STATUS_SUCCESS;
PLIST_ENTRY ListEntry = NULL;
PDIGEST_USERCONTEXT pContext = NULL;
ASSERT(!(fDerefContextHandle & fRefContextHandle)); // should not ref and deref at same time
DebugLog((DEB_TRACE_FUNC, "UserCtxtHandlerHandleToContext: Entering\n" ));
//
// Acquire exclusive access to the Context list
// For performance, this could be distributed into a set of CritSects for preventing contention
//
RtlEnterCriticalSection( &l_UserCtxtCritSect );
//
// Now walk the list of Contexts looking for a match.
//
for ( ListEntry = l_UserCtxtList.Flink;
ListEntry != &l_UserCtxtList;
ListEntry = ListEntry->Flink )
{
pContext = CONTAINING_RECORD( ListEntry, DIGEST_USERCONTEXT, Next );
//
// Found a match ... reference this Context
// (if the Context is being removed, we would increment
// and then decrement the reference, so don't bother doing
// either - since they cancel each other out).
//
DebugLog((DEB_TRACE, "UserCtxtHandlerHandleToContext: Checking context %lx for userctxt %lx\n",
pContext->LsaContext, ContextHandle ));
if (pContext->LsaContext != ContextHandle)
{
continue;
}
// Called to dereference an application SecurityContext Handle
if (fDerefContextHandle)
{
if (pContext->lReferenceHandles > 0)
{
pContext->lReferenceHandles--; // thread safe due to l_UserCtxtCritSect
}
else
{
DebugLog((DEB_ERROR, "UserCtxtHandlerHandleToContext: nonpositive App SecurityCtxt count Context = 0x%x, References = %ld, ReferenceHandles = %ld\n",
pContext, pContext->lReferences, pContext->lReferenceHandles));
Status = STATUS_OBJECT_NAME_NOT_FOUND;
*ppContext = NULL;
goto CleanUp;
}
}
// Called to add in a Reference an application SecurityContext Handle
if (fRefContextHandle)
{
pContext->lReferenceHandles++; // thread safe due to l_UserCtxtCritSect
}
DebugLog((DEB_TRACE, "UserCtxtHandlerHandleToContext: Context = 0x%x, References = %ld, ReferenceHandles = %ld\n",
pContext, pContext->lReferences, pContext->lReferenceHandles));
pContext->lReferences++; // reference counters are thread safe due to l_UserCtxtCritSect
*ppContext = pContext;
goto CleanUp;
}
//
// No match found
//
DebugLog((DEB_WARN, "UserCtxtHandlerHandleToContext: Tried to reference unknown Context 0x%lx\n", ContextHandle ));
Status = STATUS_OBJECT_NAME_NOT_FOUND;
*ppContext = NULL;
CleanUp:
RtlLeaveCriticalSection( &l_UserCtxtCritSect );
DebugLog((DEB_TRACE_FUNC, "UserCtxtHandlerHandleToContext: Leaving Status 0x%x\n", Status ));
return(Status);
}
// Check the Creation time with the Current time.
// If the difference is greater than the MAX allowed, Context is no longer valid
BOOL
UserCtxtHandlerTimeHasElapsed(
PDIGEST_USERCONTEXT pContext)
{
UNREFERENCED_PARAMETER(pContext);
return (FALSE); // no expiration at this time
}
//+--------------------------------------------------------------------
//
// Function: CtxtHandlerRelease
//
// Synopsis: Releases the Context by decreasing reference counter
//
// Arguments: pContext - pointer to credential to de-reference
//
// Returns: NTSTATUS
//
// Notes: Since multiple threads must wait for ownership
// of a context, reference count must be either 0 (unused) or 1 (in process)
//
//---------------------------------------------------------------------
NTSTATUS
UserCtxtHandlerRelease(
PDIGEST_USERCONTEXT pUserContext)
{
NTSTATUS Status = STATUS_SUCCESS;
DebugLog((DEB_TRACE_FUNC, "UserCtxtHandlerRelease: Entering\n" ));
ASSERT( pUserContext->lReferences > 0);
//
// Acquire exclusive access to the Context list
// For performance, this could be distributed into a set of CritSects for preventing contention
//
RtlEnterCriticalSection( &l_UserCtxtCritSect );
pUserContext->lReferences--;
DebugLog((DEB_TRACE, "UserCtxtHandlerRelease: UserContextInit Context 0x%x references %ld\n",
pUserContext->LsaContext, pUserContext->lReferences));
//
// If the count has dropped to zero for both application SecurityContext handles and internal SSP references
// then delink from the list and free up context
//
if (!pUserContext->lReferences && !pUserContext->lReferenceHandles)
{
DebugLog((DEB_TRACE, "UserCtxtHandlerRelease: UserContextInit unlinked and freed userContext 0x%x\n", pUserContext));
RemoveEntryList( &pUserContext->Next );
Status = UserCtxtFree(pUserContext);
if (!NT_SUCCESS (Status))
{
DebugLog((DEB_ERROR, "UserCtxtHandlerRelease: UserCtxtFree error 0x%x\n", Status));
}
}
RtlLeaveCriticalSection( &l_UserCtxtCritSect );
DebugLog((DEB_TRACE_FUNC, "UserCtxtHandlerRelease: Leaving Status 0x%x\n", Status ));
return(Status);
}
// Following functions make use of the lock for insuring single threaded operation
/*++
RoutineDescription:
Creates a new DACL for the token granting the server and client
all access to the token.
Arguments:
Token - Handle to an impersonation token open for TOKEN_QUERY and
WRITE_DAC
Return Value:
STATUS_INSUFFICIENT_RESOURCES - insufficient memory to complete
the function.
Errors from NtSetSecurityObject
--*/
NTSTATUS
SspCreateTokenDacl(
HANDLE Token
)
{
NTSTATUS Status = STATUS_SUCCESS;
PTOKEN_USER ProcessTokenUser = NULL;
PTOKEN_USER ThreadTokenUser = NULL;
PTOKEN_USER ImpersonationTokenUser = NULL;
HANDLE ProcessToken = NULL;
HANDLE ImpersonationToken = NULL;
BOOL fInsertImpersonatingUser = FALSE;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
ULONG AclLength = 0;
PACL NewDacl = NULL;
SECURITY_DESCRIPTOR SecurityDescriptor;
BOOL fReleaseContextLock = FALSE;
DebugLog((DEB_TRACE_FUNC, "SspCreateTokenDacl: Entering Token is 0x%x\n", Token));
//
// Build the two well known sids we need.
//
if (g_NtDigestGlobalLocalSystemSid == NULL || g_NtDigestGlobalAliasAdminsSid == NULL ) {
RtlEnterCriticalSection(&l_UserCtxtCritSect);
fReleaseContextLock = TRUE;
if (g_NtDigestGlobalLocalSystemSid == NULL)
{
PSID pLocalSidSystem = NULL;
DebugLog((DEB_TRACE, "SspCreateTokenDacl: Allocate and Init LocalSystem SID\n"));
Status = RtlAllocateAndInitializeSid(
&NtAuthority,
1,
SECURITY_LOCAL_SYSTEM_RID,
0,0,0,0,0,0,0,
&pLocalSidSystem
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "SspCreateTokenDacl: RtlAllocateAndInitializeSid returns 0x%lx\n", Status ));
goto Cleanup;
}
DebugLog((DEB_TRACE, "SspCreateTokenDacl: Allocate and Init LocalSystem SID DONE\n"));
g_NtDigestGlobalLocalSystemSid = pLocalSidSystem;
}
if (g_NtDigestGlobalAliasAdminsSid == NULL)
{
PSID pLocalSidAdmins = NULL;
DebugLog((DEB_TRACE, "SspCreateTokenDacl: Allocate and Init AliasAdmin SID\n"));
Status = RtlAllocateAndInitializeSid(
&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0,0,0,0,0,0,
&pLocalSidAdmins
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "SspCreateTokenDacl, RtlAllocateAndInitializeSid returns 0x%lx\n", Status ));
goto Cleanup;
}
DebugLog((DEB_TRACE, "SspCreateTokenDacl: Allocate and Init AliasAdmin SID DONE\n"));
g_NtDigestGlobalAliasAdminsSid = pLocalSidAdmins;
}
RtlLeaveCriticalSection(&l_UserCtxtCritSect);
fReleaseContextLock = FALSE;
}
//
// it's possible that the current thread is impersonating a user.
// if that's the case, get it's token user, and revert to insure we
// can open the process token.
//
Status = NtOpenThreadToken(
NtCurrentThread(),
TOKEN_QUERY | TOKEN_IMPERSONATE,
TRUE,
&ImpersonationToken
);
if( NT_SUCCESS(Status) )
{
//
// stop impersonating.
//
RevertToSelf();
//
// get the token user for the impersonating user.
//
Status = SspGetTokenUser(
ImpersonationToken,
&ImpersonationTokenUser
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "SspCreateTokenDacl: SspGetTokenUser (1) returns 0x%lx\n", Status ));
goto Cleanup;
}
}
//
// Open the process token to find out the user sid
//
Status = NtOpenProcessToken(
NtCurrentProcess(),
TOKEN_QUERY,
&ProcessToken
);
if(!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "SspCreateTokenDacl: NtOpenProcessToken returns 0x%lx\n", Status ));
goto Cleanup;
}
//
// get the token user for the process token.
//
Status = SspGetTokenUser(
ProcessToken,
&ProcessTokenUser
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "SspCreateTokenDacl: SspGetTokenUser (2) returns 0x%lx\n", Status ));
goto Cleanup;
}
//
// Now get the token user for the thread.
//
Status = SspGetTokenUser(
Token,
&ThreadTokenUser
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "SspCreateTokenDacl: SspGetTokenUser (3) returns 0x%lx\n", Status ));
goto Cleanup;
}
AclLength = 4 * sizeof( ACCESS_ALLOWED_ACE ) - 4 * sizeof( ULONG ) +
RtlLengthSid( ProcessTokenUser->User.Sid ) +
RtlLengthSid( ThreadTokenUser->User.Sid ) +
RtlLengthSid( g_NtDigestGlobalLocalSystemSid ) +
RtlLengthSid( g_NtDigestGlobalAliasAdminsSid ) +
sizeof( ACL );
//
// determine if we need to add impersonation token sid onto the token Dacl.
//
if( ImpersonationTokenUser &&
!RtlEqualSid( ImpersonationTokenUser->User.Sid, ProcessTokenUser->User.Sid ) &&
!RtlEqualSid( ImpersonationTokenUser->User.Sid, ThreadTokenUser->User.Sid )
)
{
AclLength += (sizeof(ACCESS_ALLOWED_ACE) - sizeof( ULONG )) +
RtlLengthSid( ImpersonationTokenUser->User.Sid );
fInsertImpersonatingUser = TRUE;
}
NewDacl = (PACL)DigestAllocateMemory(AclLength );
if (NewDacl == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
DebugLog((DEB_ERROR, "SspCreateTokenDacl: NtLmallocate returns 0x%lx\n", NewDacl));
goto Cleanup;
}
Status = RtlCreateAcl( NewDacl, AclLength, ACL_REVISION2 );
ASSERT(NT_SUCCESS( Status ));
Status = RtlAddAccessAllowedAce (
NewDacl,
ACL_REVISION2,
TOKEN_ALL_ACCESS,
ProcessTokenUser->User.Sid
);
ASSERT( NT_SUCCESS( Status ));
Status = RtlAddAccessAllowedAce (
NewDacl,
ACL_REVISION2,
TOKEN_ALL_ACCESS,
ThreadTokenUser->User.Sid
);
ASSERT( NT_SUCCESS( Status ));
if( fInsertImpersonatingUser )
{
Status = RtlAddAccessAllowedAce (
NewDacl,
ACL_REVISION2,
TOKEN_ALL_ACCESS,
ImpersonationTokenUser->User.Sid
);
ASSERT( NT_SUCCESS( Status ));
}
Status = RtlAddAccessAllowedAce (
NewDacl,
ACL_REVISION2,
TOKEN_ALL_ACCESS,
g_NtDigestGlobalAliasAdminsSid
);
ASSERT( NT_SUCCESS( Status ));
Status = RtlAddAccessAllowedAce (
NewDacl,
ACL_REVISION2,
TOKEN_ALL_ACCESS,
g_NtDigestGlobalLocalSystemSid
);
ASSERT( NT_SUCCESS( Status ));
Status = RtlCreateSecurityDescriptor (
&SecurityDescriptor,
SECURITY_DESCRIPTOR_REVISION
);
ASSERT( NT_SUCCESS( Status ));
Status = RtlSetDaclSecurityDescriptor(
&SecurityDescriptor,
TRUE,
NewDacl,
FALSE
);
ASSERT( NT_SUCCESS( Status ));
Status = NtSetSecurityObject(
Token,
DACL_SECURITY_INFORMATION,
&SecurityDescriptor
);
ASSERT( NT_SUCCESS( Status ));
Cleanup:
if( fReleaseContextLock )
RtlLeaveCriticalSection(&l_UserCtxtCritSect);
if (ImpersonationToken != NULL)
{
//
// put the thread token back if we were impersonating.
//
(void)SetThreadToken( NULL, ImpersonationToken );
NtClose(ImpersonationToken);
}
if (ThreadTokenUser != NULL) {
DigestFreeMemory( ThreadTokenUser );
}
if (ProcessTokenUser != NULL) {
DigestFreeMemory( ProcessTokenUser );
}
if (ImpersonationTokenUser != NULL) {
DigestFreeMemory( ImpersonationTokenUser );
}
if (NewDacl != NULL) {
DigestFreeMemory( NewDacl );
}
if (ProcessToken != NULL)
{
NtClose(ProcessToken);
}
DebugLog((DEB_TRACE_FUNC, "SspCreateTokenDacl: Leaving Token is 0x%x\n", Token));
return( Status );
}