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.
611 lines
18 KiB
611 lines
18 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->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->ustrDomainName));
|
|
|
|
// Erase any password information
|
|
if (((pDigestCred->ustrPassword).MaximumLength) &&
|
|
((pDigestCred->ustrPassword).Buffer))
|
|
{
|
|
SecureZeroMemory((pDigestCred->ustrPassword).Buffer, (pDigestCred->ustrPassword).MaximumLength);
|
|
}
|
|
UnicodeStringFree(&(pDigestCred->ustrPassword));
|
|
UnicodeStringFree(&(pDigestCred->ustrDomain));
|
|
UnicodeStringFree(&(pDigestCred->ustrUpn));
|
|
|
|
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: Dereferencing 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: Dereferencing 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: Dereferencing 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, "CredHandlerRelease: 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)
|
|
{
|
|
SecureZeroMemory((pCredential->ustrPassword).Buffer, (pCredential->ustrPassword).MaximumLength);
|
|
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)
|
|
{
|
|
SecureZeroMemory(pustrPasswd->Buffer, pustrPasswd->MaximumLength);
|
|
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);
|
|
}
|
|
|