Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

612 lines
17 KiB

//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 2000
//
// File: creds.cxx
//
// Contents: Credential functions:
//
//
// History: KDamour 15Mar00 Stolen from NTLM
//
//------------------------------------------------------------------------
#include "global.h"
//
// Crit Sect to protect various globals in this module.
//
RTL_CRITICAL_SECTION l_CredentialCritSect;
LIST_ENTRY l_CredentialList;
// Simple variable to make sure that the package was initialize
BOOL g_bCredentialsInitialized = FALSE;
//+--------------------------------------------------------------------
//
// Function: CredHandlerInit
//
// Synopsis: Initializes the credential manager package
//
// Arguments: none
//
// Returns: NTSTATUS
//
// Notes: Called by SpInitialize
//
//---------------------------------------------------------------------
NTSTATUS
CredHandlerInit(VOID)
{
NTSTATUS Status = STATUS_SUCCESS;
//
// Initialize the Credential list to be empty.
//
Status = RtlInitializeCriticalSection(&l_CredentialCritSect);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "CredHandlerInit: Failed to initialize critsec 0x%x\n", Status));
goto CleanUp;
}
InitializeListHead( &l_CredentialList );
// Simple variable test to make sure all initialized;
g_bCredentialsInitialized = TRUE;
CleanUp:
return Status;
}
NTSTATUS
CredHandlerInsertCred(
IN PDIGEST_CREDENTIAL pDigestCred
)
{
RtlEnterCriticalSection( &l_CredentialCritSect );
pDigestCred->Unlinked = FALSE;
InsertHeadList( &l_CredentialList, &pDigestCred->Next );
RtlLeaveCriticalSection( &l_CredentialCritSect );
return STATUS_SUCCESS;
}
// Initialize the Credential Structure
NTSTATUS
CredentialInit(
IN PDIGEST_CREDENTIAL pDigestCred)
{
NTSTATUS Status = STATUS_SUCCESS;
ASSERT(pDigestCred);
if (!pDigestCred)
{
return STATUS_INVALID_PARAMETER;
}
ZeroMemory(pDigestCred, sizeof(DIGEST_CREDENTIAL));
pDigestCred->TimeCreated = time(NULL);
pDigestCred->Unlinked = TRUE;
pDigestCred->CredentialHandle = (ULONG_PTR)pDigestCred;
pDigestCred->lReferences = 0;
return(Status);
}
// Free up memory utilized by Credential the Credential Structure
NTSTATUS
CredentialFree(
IN PDIGEST_CREDENTIAL pDigestCred)
{
NTSTATUS Status = STATUS_SUCCESS;
ASSERT(pDigestCred);
ASSERT(0 == pDigestCred->lReferences);
UnicodeStringFree(&(pDigestCred->ustrAccountName));
UnicodeStringFree(&(pDigestCred->ustrDownlevelName));
UnicodeStringFree(&(pDigestCred->ustrDomainName));
// Erase any password information
if (((pDigestCred->ustrPassword).Length) &&
((pDigestCred->ustrPassword).Buffer))
{
ZeroMemory((pDigestCred->ustrPassword).Buffer, (pDigestCred->ustrPassword).MaximumLength);
}
UnicodeStringFree(&(pDigestCred->ustrPassword));
UnicodeStringFree(&(pDigestCred->ustrDnsDomainName));
UnicodeStringFree(&(pDigestCred->ustrUpn));
UnicodeStringFree(&(pDigestCred->ustrLogonServer));
DigestFreeMemory(pDigestCred);
return(Status);
}
/*++
Routine Description:
This routine checks to see if the Credential Handle is from a currently
active client, and references the Credential if it is valid.
The caller can request that the Credential be dereferenced only.
For a client's Credential to be valid, the Credential value
must be on our list of active Credentials.
Arguments:
CredentialHandle - Points to the CredentialHandle of the Credential
to be referenced.
DereferenceCredential - This boolean value indicates whether the caller
wants the logon process's Credential to be referenced (FALSE) or
decremented (TRUE)
Return Value:
NULL - the Credential was not found.
Otherwise - returns a pointer to the referenced credential.
--*/
NTSTATUS
CredHandlerHandleToPtr(
IN ULONG_PTR CredentialHandle,
IN BOOLEAN DereferenceCredential,
OUT PDIGEST_CREDENTIAL * UserCredential
)
{
PLIST_ENTRY ListEntry = NULL;
PDIGEST_CREDENTIAL Credential = NULL;
NTSTATUS Status = STATUS_SUCCESS;
SECPKG_CLIENT_INFO ClientInfo;
SECPKG_CALL_INFO CallInfo;
// LONG lDereferenceCount = 1;
ULONG ulDereferenceCount = 1;
LONG lReferences = 0;
DebugLog((DEB_TRACE_FUNC, "CredHandlerHandleToPtr: Entering Credential 0x%x\n", CredentialHandle));
// set default output
ASSERT(UserCredential);
*UserCredential = NULL ;
ZeroMemory( &CallInfo, sizeof(CallInfo) );
ZeroMemory( &ClientInfo, sizeof(ClientInfo) );
if(g_LsaFunctions->GetCallInfo(&CallInfo))
{
ulDereferenceCount = CallInfo.CallCount;
DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: CallCount 0x%x\n", CallInfo.CallCount));
DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: Attributes 0x%x\n", CallInfo.Attributes));
DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: PID %d Thread %d\n", CallInfo.ProcessId, CallInfo.ThreadId));
}
else
{
ZeroMemory( &CallInfo, sizeof(CallInfo) );
}
Status = g_LsaFunctions->GetClientInfo(&ClientInfo);
if(!NT_SUCCESS(Status))
{
//
// this call can fail during a cleanup call. so ignore that for now,
// and check for cleanup disposition.
//
if ((CallInfo.Attributes & SECPKG_CALL_CLEANUP) != 0)
{
Status = STATUS_SUCCESS;
RtlZeroMemory(
&ClientInfo,
sizeof(SECPKG_CLIENT_INFO)
);
ClientInfo.HasTcbPrivilege = TRUE;
ClientInfo.ProcessID = CallInfo.ProcessId;
}
if( !NT_SUCCESS( Status ) )
{
DebugLog(( DEB_ERROR, "CredHandlerHandleToPtr: GetClientInfo returned 0x%lx\n", Status));
return( Status );
}
}
if( CallInfo.Attributes & SECPKG_CALL_CLEANUP )
{
DebugLog(( DEB_TRACE, "CredHandlerHandleToPtr: Cleanup Called pid: 0x%lx handle: %p refcount: %lu\n",
ClientInfo.ProcessID, CredentialHandle, ulDereferenceCount));
}
//
// Acquire exclusive access to the Credential list
//
RtlEnterCriticalSection( &l_CredentialCritSect );
//
// Now walk the list of Credentials looking for a match.
//
for ( ListEntry = l_CredentialList.Flink;
ListEntry != &l_CredentialList;
ListEntry = ListEntry->Flink )
{
Credential = CONTAINING_RECORD( ListEntry, DIGEST_CREDENTIAL, Next );
//
// Found a match ... reference this Credential
// (if the Credential is being removed, we would increment
// and then decrement the reference, so don't bother doing
// either - since they cancel each other out).
//
if (( Credential == (PDIGEST_CREDENTIAL) CredentialHandle))
{
// Make sure we have the privilege of accessing
// this handle
if (!ClientInfo.HasTcbPrivilege &&
(Credential->ClientProcessID != ClientInfo.ProcessID)
)
{
DebugLog((DEB_ERROR, "CredHandlerHandleToPtr: ProcessIDs are different. Access forbidden.\n"));
break;
}
if (!DereferenceCredential)
{
lReferences = InterlockedIncrement(&Credential->lReferences);
DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: Incremented ReferenceCount %ld\n", lReferences));
}
else
{
DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: Derefencing credential\n" ));
ASSERT((ulDereferenceCount > 0));
// Note: Subtract one off of the deref count, this avoids an extra interlock operation
// After exit, SpFreeCredentialsHandle will call CredHandlerRelease
ulDereferenceCount--;
if( ulDereferenceCount == 1 )
{
DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: Derefencing by one count\n" ));
lReferences = InterlockedDecrement( &Credential->lReferences );
ASSERT( (lReferences > 0) );
}
else if( ulDereferenceCount > 1 )
{
//
// there is no equivalent to InterlockedSubtract.
// so, turn it into an Add with some signed magic.
//
LONG lDecrementToIncrement = 0 - ulDereferenceCount;
DebugLog((DEB_TRACE, "CredHandlerHandleToPtr: Derefencing by %lu count\n", ulDereferenceCount ));
lReferences = InterlockedExchangeAdd( &Credential->lReferences, lDecrementToIncrement );
lReferences += lDecrementToIncrement;
ASSERT( (lReferences > 0) );
}
}
// Found the Credential
*UserCredential = Credential ;
goto CleanUp;
}
}
//
// No match found
//
DebugLog((DEB_WARN, "CredHandlerHandleToCredential: Tried to reference unknown Credential 0x%lx\n",
CredentialHandle ));
Status = STATUS_INVALID_HANDLE;
CleanUp:
RtlLeaveCriticalSection( &l_CredentialCritSect );
DebugLog((DEB_TRACE_FUNC, "CredHandlerHandleToPtr: Leaving Credential 0x%x\n", CredentialHandle));
return(Status);
}
// Locate a Credential based on a LogonId, ProcessID
// For either the Logon list or the Credential list
NTSTATUS
CredHandlerLocatePtr(
IN PLUID pLogonId,
IN ULONG CredentialUseFlags,
OUT PDIGEST_CREDENTIAL * UserCredential
)
{
PLIST_ENTRY ListEntry = NULL;
PDIGEST_CREDENTIAL Credential = NULL;
NTSTATUS Status = STATUS_SUCCESS;
// SECPKG_CLIENT_INFO ClientInfo;
SECPKG_CALL_INFO CallInfo;
LONG lReferences = 0;
DebugLog((DEB_TRACE_FUNC, "CredHandlerLocatePtr: Entering\n"));
*UserCredential = NULL ;
// If we do not have a LogonId
if (!pLogonId)
{
return(STATUS_INVALID_HANDLE);
}
//
// Match both flags
//
// CredentialUseFlags |= CredentialFlags;
if (!g_LsaFunctions->GetCallInfo(&CallInfo))
{
DebugLog((DEB_ERROR,"CredHandlerLocatePtr: Failed to get call info\n"));
return(STATUS_INVALID_HANDLE); // Really this is another error
}
//
// Acquire exclusive access to the Credential list
//
RtlEnterCriticalSection( &l_CredentialCritSect );
//
// Now walk the list of Credentials looking for a match.
//
for ( ListEntry = l_CredentialList.Flink;
ListEntry != &l_CredentialList;
ListEntry = ListEntry->Flink )
{
Credential = CONTAINING_RECORD( ListEntry, DIGEST_CREDENTIAL, Next );
//
// Found a match ... reference this Credential
// (if the Credential is being removed, we would increment
// and then decrement the reference, so don't bother doing
// either - since they cancel each other out).
//
// If this is a session credential then check for appropriate flags (like inbound or outbound)
if ((Credential->CredentialUseFlags & DIGEST_CRED_MATCH_FLAGS) != CredentialUseFlags)
{
continue;
}
if (RtlEqualLuid(&(Credential->LogonId), pLogonId) &&
(Credential->ClientProcessID == CallInfo.ProcessId))
{
lReferences = InterlockedIncrement(&Credential->lReferences);
DebugLog((DEB_TRACE, "CredHandlerLocatePtr: ReferenceCount %ld\n", lReferences));
// Found the Credential
*UserCredential = Credential ;
goto CleanUp;
}
}
//
// No match found
//
DebugLog((DEB_WARN, "CredHandlerLocatePtr: Tried to reference unknown LogonId (%x:%lx)\n",
pLogonId->HighPart, pLogonId->LowPart ));
Status = STATUS_INVALID_HANDLE;
CleanUp:
RtlLeaveCriticalSection( &l_CredentialCritSect );
DebugLog((DEB_TRACE_FUNC, "CredHandlerLocatePtr: Leaving Status 0x%x\n", Status));
return(Status);
}
//+--------------------------------------------------------------------
//
// Function: CredHandlerRelease
//
// Synopsis: Releases the Credential by decreasing reference counter
// if Credential reference count drops to zero, Credential is deleted
//
// Arguments: pCredential - pointer to credential to de-reference
//
// Returns: NTSTATUS
//
// Notes: Called by ASC. Since multiple threads can have a credential
// checked out, simply decrease the reference counter on release.
//
//---------------------------------------------------------------------
NTSTATUS
CredHandlerRelease(
PDIGEST_CREDENTIAL pCredential)
{
NTSTATUS Status = STATUS_SUCCESS;
LONG lReferences = 0;
DebugLog((DEB_TRACE_FUNC, "CredHandlerRelease: Entering for Credential 0x%0x\n", pCredential));
lReferences = InterlockedDecrement(&pCredential->lReferences);
DebugLog((DEB_TRACE, "CredHandlerRelease: ReferenceCount %ld\n", lReferences));
ASSERT( lReferences >= 0 );
//
// If the count has dropped to zero, then free all alloced stuff
// Care must be taken since Cred is still in linked list - need to grab critsec
// and then test again if zero since another thread might have taken a ref to cred
//
if (lReferences == 0)
{
RtlEnterCriticalSection( &l_CredentialCritSect );
// Check to make sure no one took a reference since InterlockDecrement
if (pCredential->lReferences)
{
DebugLog((DEB_TRACE, "CredHandlerRelease: Another thread took a reference. No action taken\n"));
}
else
{
// Safe to remove from list and delete
// Check if added into linked list
if (!pCredential->Unlinked)
{
RemoveEntryList( &pCredential->Next );
DebugLog((DEB_TRACE, "CredHandlerRelease: Unlinked Credential 0x%x\n", pCredential));
}
DebugLog((DEB_TRACE, "CredHandlerRelease: Deleting Credential 0x%x\n", pCredential));
Status = CredentialFree(pCredential);
}
RtlLeaveCriticalSection( &l_CredentialCritSect );
}
DebugLog((DEB_TRACE_FUNC, "LogSessHandlerRelease: Leaving Status 0x%x\n", Status));
return(Status);
}
// Helper functions for processing fields within the credentials
//+--------------------------------------------------------------------
//
// Function: CredHandlerPasswdSet
//
// Synopsis: Set the unicode string password in the credential
//
// Arguments: pCredential - pointer to credential to use
// pustrPasswd - pointer to new password
//
// Returns: NTSTATUS
//
// Notes: might want to use record locking in the future instead
// of an update flag on credentials
//
//---------------------------------------------------------------------
NTSTATUS
CredHandlerPasswdSet(
IN OUT PDIGEST_CREDENTIAL pCredential,
IN PUNICODE_STRING pustrPasswd)
{
NTSTATUS Status = STATUS_SUCCESS;
// Protect writing the info into the credential
RtlEnterCriticalSection( &l_CredentialCritSect );
if (pCredential->ustrPassword.Buffer)
{
UnicodeStringFree(&(pCredential->ustrPassword));
}
Status = UnicodeStringDuplicatePassword(&(pCredential->ustrPassword),pustrPasswd);
RtlLeaveCriticalSection( &l_CredentialCritSect );
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "CredHandlerPasswdSet: Error in setting Credential password, status 0x%0x\n", Status ));
}
return(Status);
}
//+--------------------------------------------------------------------
//
// Function: CredHandlerPasswdGet
//
// Synopsis: Get the unicode string password in the credential
// Locking is only necessary for the logon creds and not the session creds
// but it is just as well to keep it unifom
//
// Arguments: pCredential - pointer to credential to use
// pustrPasswd - pointer to destination copy of password
//
// Returns: NTSTATUS
//
// Notes:
//
//---------------------------------------------------------------------
NTSTATUS
CredHandlerPasswdGet(
IN PDIGEST_CREDENTIAL pCredential,
OUT PUNICODE_STRING pustrPasswd)
{
NTSTATUS Status = STATUS_SUCCESS;
// Protect reading/writing the credential password
RtlEnterCriticalSection( &l_CredentialCritSect );
if (pustrPasswd->Buffer)
{
UnicodeStringFree(pustrPasswd);
}
Status = UnicodeStringDuplicatePassword(pustrPasswd, &(pCredential->ustrPassword));
RtlLeaveCriticalSection( &l_CredentialCritSect );
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR, "CredHandlerPasswdSet: Error in setting Credential password, status 0x%0x\n", Status ));
}
return(Status);
}