1401 lines
36 KiB
C++
1401 lines
36 KiB
C++
|
//+-----------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation 1992 - 1996
|
||
|
//
|
||
|
// File: credmgr.cxx
|
||
|
//
|
||
|
// Contents: Code for managing credentials list for the Kerberos package
|
||
|
//
|
||
|
//
|
||
|
// History: 17-April-1996 Created MikeSw
|
||
|
// 26-Sep-1998 ChandanS
|
||
|
// Added more debugging support etc.
|
||
|
//
|
||
|
//------------------------------------------------------------------------
|
||
|
|
||
|
#include <kerb.hxx>
|
||
|
#define CREDMGR_ALLOCATE
|
||
|
#include <kerbp.h>
|
||
|
#ifdef RETAIL_LOG_SUPPORT
|
||
|
static TCHAR THIS_FILE[]=TEXT(__FILE__);
|
||
|
#endif
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbInitCredentialList
|
||
|
//
|
||
|
// Synopsis: Initializes the Credentials list
|
||
|
//
|
||
|
// Effects: allocates a resources
|
||
|
//
|
||
|
// Arguments: none
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: STATUS_SUCCESS on success, other error codes
|
||
|
// on failure
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
NTSTATUS
|
||
|
KerbInitCredentialList(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
|
||
|
|
||
|
Status = KerbInitializeList( &KerbCredentialList );
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
KerberosCredentialsInitialized = TRUE;
|
||
|
|
||
|
Cleanup:
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
KerbFreeList( &KerbCredentialList);
|
||
|
|
||
|
}
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFreeCredentialList
|
||
|
//
|
||
|
// Synopsis: Frees the credentials list
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: none
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: none
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
VOID
|
||
|
KerbFreeCredentialList(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
PKERB_CREDENTIAL Credential;
|
||
|
|
||
|
|
||
|
if (KerberosCredentialsInitialized)
|
||
|
{
|
||
|
KerbLockList(&KerbCredentialList);
|
||
|
|
||
|
//
|
||
|
// Go through the list of logon sessions and dereferences them all
|
||
|
//
|
||
|
|
||
|
while (!IsListEmpty(&KerbCredentialList.List))
|
||
|
{
|
||
|
Credential = CONTAINING_RECORD(
|
||
|
KerbCredentialList.List.Flink,
|
||
|
KERB_CREDENTIAL,
|
||
|
ListEntry.Next
|
||
|
);
|
||
|
|
||
|
KerbReferenceListEntry(
|
||
|
&KerbCredentialList,
|
||
|
&Credential->ListEntry,
|
||
|
TRUE
|
||
|
);
|
||
|
|
||
|
KerbDereferenceCredential(Credential);
|
||
|
|
||
|
}
|
||
|
|
||
|
KerbFreeList(&KerbCredentialList);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbAllocateCredential
|
||
|
//
|
||
|
// Synopsis: Allocates a credential structure
|
||
|
//
|
||
|
// Effects: Allocates a credential, but does not add it to the
|
||
|
// list of credentials
|
||
|
//
|
||
|
// Arguments: NewCredential - receives a new credential allocated
|
||
|
// with KerbAllocate
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: STATUS_SUCCESS on success
|
||
|
// STATUS_INSUFFICIENT_RESOURCES if the allocation fails
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
NTSTATUS
|
||
|
KerbAllocateCredential(
|
||
|
PKERB_CREDENTIAL * NewCredential
|
||
|
)
|
||
|
{
|
||
|
PKERB_CREDENTIAL Credential;
|
||
|
SECPKG_CALL_INFO CallInfo;
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
|
||
|
*NewCredential = NULL;
|
||
|
|
||
|
if (!LsaFunctions->GetCallInfo(&CallInfo))
|
||
|
{
|
||
|
D_DebugLog((DEB_ERROR,"Failed to get call info. %ws, line %d\n",
|
||
|
THIS_FILE, __LINE__));
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
Credential = (PKERB_CREDENTIAL) KerbAllocate(
|
||
|
sizeof(KERB_CREDENTIAL) );
|
||
|
|
||
|
if (Credential == NULL)
|
||
|
{
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
Credential->ClientProcess = CallInfo.ProcessId;
|
||
|
|
||
|
KerbInitializeListEntry(
|
||
|
&Credential->ListEntry
|
||
|
);
|
||
|
//
|
||
|
// Set the references to 1 since we are returning a pointer to the
|
||
|
// logon session
|
||
|
//
|
||
|
|
||
|
Credential->HandleCount = 1;
|
||
|
|
||
|
*NewCredential = Credential;
|
||
|
|
||
|
Cleanup:
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbInsertCredential
|
||
|
//
|
||
|
// Synopsis: Inserts a logon session into the list of logon sessions
|
||
|
//
|
||
|
// Effects: bumps reference count on logon session
|
||
|
//
|
||
|
// Arguments: Credential - Credential to insert
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: STATUS_SUCCESS always
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
KerbInsertCredential(
|
||
|
IN PKERB_CREDENTIAL Credential
|
||
|
)
|
||
|
{
|
||
|
|
||
|
//
|
||
|
// insert entry at tail of list.
|
||
|
// reason: entries at the head are more likely to have _TGT flag set
|
||
|
// and, those are the ones we want to satisfy from cache for repeat
|
||
|
// high stress offenders...
|
||
|
//
|
||
|
|
||
|
Credential->CredentialTag = KERB_CREDENTIAL_TAG_ACTIVE;
|
||
|
|
||
|
KerbInsertListEntryTail(
|
||
|
&Credential->ListEntry,
|
||
|
&KerbCredentialList
|
||
|
);
|
||
|
|
||
|
return(STATUS_SUCCESS);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFreePrimaryCredentials
|
||
|
//
|
||
|
// Synopsis: frees a primary credentials structure
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
VOID
|
||
|
KerbFreePrimaryCredentials(
|
||
|
IN PKERB_PRIMARY_CREDENTIAL Credentials,
|
||
|
IN BOOLEAN FreeBaseStructure
|
||
|
)
|
||
|
{
|
||
|
if (Credentials != NULL)
|
||
|
{
|
||
|
KerbFreeString(&Credentials->DomainName);
|
||
|
KerbFreeString(&Credentials->OldDomainName);
|
||
|
KerbFreeString(&Credentials->UserName);
|
||
|
KerbFreeString(&Credentials->OldUserName);
|
||
|
|
||
|
RtlZeroMemory( &Credentials->OldHashPassword, sizeof(Credentials->OldHashPassword) );
|
||
|
if (Credentials->ClearPassword.Buffer != NULL)
|
||
|
{
|
||
|
RtlZeroMemory(
|
||
|
Credentials->ClearPassword.Buffer,
|
||
|
Credentials->ClearPassword.Length
|
||
|
);
|
||
|
KerbFreeString(&Credentials->ClearPassword);
|
||
|
RtlZeroMemory(&Credentials->ClearPassword, sizeof(Credentials->ClearPassword));
|
||
|
}
|
||
|
if (Credentials->Passwords != NULL)
|
||
|
{
|
||
|
KerbFreeStoredCred(Credentials->Passwords);
|
||
|
}
|
||
|
if (Credentials->OldPasswords != NULL)
|
||
|
{
|
||
|
KerbFreeStoredCred(Credentials->OldPasswords);
|
||
|
}
|
||
|
KerbPurgeTicketCache(&Credentials->ServerTicketCache);
|
||
|
KerbPurgeTicketCache(&Credentials->AuthenticationTicketCache);
|
||
|
|
||
|
KerbFreePKCreds(Credentials->PublicKeyCreds);
|
||
|
Credentials->PublicKeyCreds = NULL;
|
||
|
|
||
|
if (FreeBaseStructure)
|
||
|
{
|
||
|
KerbFree(Credentials);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbFreeCredential
|
||
|
//
|
||
|
// Synopsis: Frees a credential structure and all contained data
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: Credential - The credential to free.
|
||
|
//
|
||
|
// Requires: This credential must be unlinked
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
VOID
|
||
|
KerbFreeCredential(
|
||
|
IN PKERB_CREDENTIAL Credential
|
||
|
)
|
||
|
{
|
||
|
|
||
|
Credential->CredentialTag = KERB_CREDENTIAL_TAG_DELETE;
|
||
|
|
||
|
if (Credential->SuppliedCredentials != NULL)
|
||
|
{
|
||
|
KerbFreePrimaryCredentials(Credential->SuppliedCredentials, TRUE);
|
||
|
}
|
||
|
DsysAssert(Credential->ListEntry.Next.Flink == NULL);
|
||
|
KerbFreeString(&Credential->CredentialName);
|
||
|
|
||
|
KerbFree(Credential);
|
||
|
|
||
|
}
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbGetTicketForCredential
|
||
|
//
|
||
|
// Synopsis: Obtains a TGT for a credential if it doesn't already
|
||
|
// have one.
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
KerbGetTicketForCredential(
|
||
|
IN OPTIONAL PKERB_LOGON_SESSION LogonSession,
|
||
|
IN PKERB_CREDENTIAL Credential,
|
||
|
IN OPTIONAL PKERB_CREDMAN_CRED CredManCredentials,
|
||
|
IN OPTIONAL PUNICODE_STRING SuppRealm
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
PKERB_LOGON_SESSION LocalLogonSession = NULL;
|
||
|
BOOLEAN GetRestrictedTgt = FALSE;
|
||
|
PKERB_TICKET_CACHE_ENTRY Tgt = NULL;
|
||
|
BOOLEAN PrimaryLogonSessionCredential = FALSE;
|
||
|
|
||
|
//
|
||
|
// We've got to make a determination w.r.t. whether this
|
||
|
// is an attempt to renew our primary credential.
|
||
|
// This will affect our logon session flags.
|
||
|
//
|
||
|
|
||
|
|
||
|
if (!ARGUMENT_PRESENT(LogonSession))
|
||
|
{
|
||
|
LocalLogonSession = KerbReferenceLogonSession(
|
||
|
&Credential->LogonId,
|
||
|
FALSE // don't unlink
|
||
|
);
|
||
|
if (LocalLogonSession == NULL)
|
||
|
{
|
||
|
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
LocalLogonSession = LogonSession;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Here we make the assumption that if we didn't get a credential
|
||
|
// and we also got a logon session, then we're dealing w/ the
|
||
|
// logon session's primary credential
|
||
|
//
|
||
|
if ((ARGUMENT_PRESENT(Credential)) &&
|
||
|
(Credential->SuppliedCredentials == NULL) &&
|
||
|
(!ARGUMENT_PRESENT(CredManCredentials)))
|
||
|
|
||
|
{
|
||
|
PrimaryLogonSessionCredential = TRUE;
|
||
|
D_DebugLog((DEB_TRACE_CRED, "Getting Credentials for primary logon session - %x\n", LogonSession));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
D_DebugLog((DEB_TRACE_CRED, "Got a credential && a logon session\n"));
|
||
|
}
|
||
|
|
||
|
Status = KerbGetTicketGrantingTicket(
|
||
|
LocalLogonSession,
|
||
|
Credential,
|
||
|
CredManCredentials,
|
||
|
SuppRealm,
|
||
|
&Tgt,
|
||
|
NULL // don't return credential key
|
||
|
);
|
||
|
|
||
|
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
|
||
|
}
|
||
|
|
||
|
KerbWriteLockLogonSessions(LocalLogonSession);
|
||
|
|
||
|
//
|
||
|
// Clear the logon deferred bit for the logon session, if set
|
||
|
// Note: This will only be cleared as a result of refreshing
|
||
|
// logon session's primary cred tgt
|
||
|
//
|
||
|
if (PrimaryLogonSessionCredential &&
|
||
|
((LocalLogonSession->LogonSessionFlags & KERB_LOGON_DEFERRED) != 0))
|
||
|
{
|
||
|
LocalLogonSession->LogonSessionFlags &= ~KERB_LOGON_DEFERRED;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we have a credential, be sure to set the TGT_avail bit for
|
||
|
// those credentials.
|
||
|
//
|
||
|
if (ARGUMENT_PRESENT(Credential))
|
||
|
{
|
||
|
Credential->CredentialFlags |= KERB_CRED_TGT_AVAIL;
|
||
|
if ((Credential->CredentialFlags & KERB_CRED_RESTRICTED) != 0)
|
||
|
{
|
||
|
GetRestrictedTgt = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (ARGUMENT_PRESENT(CredManCredentials))
|
||
|
{
|
||
|
CredManCredentials->CredentialFlags |= KERB_CRED_TGT_AVAIL;
|
||
|
}
|
||
|
|
||
|
KerbUnlockLogonSessions(LocalLogonSession);
|
||
|
|
||
|
#ifdef RESTRICTED_TOKEN
|
||
|
if (GetRestrictedTgt)
|
||
|
{
|
||
|
Status = KerbGetRestrictedTgtForCredential(
|
||
|
LocalLogonSession,
|
||
|
Credential
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to get restricted TGT for credential: 0x%x\n",Status));
|
||
|
|
||
|
KerbRemoveTicketCacheEntry(Tgt);
|
||
|
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
Cleanup:
|
||
|
|
||
|
//
|
||
|
// Reset the bits if we failed
|
||
|
//
|
||
|
|
||
|
if (LocalLogonSession && !NT_SUCCESS(Status))
|
||
|
{
|
||
|
KerbWriteLockLogonSessions(LocalLogonSession);
|
||
|
|
||
|
//
|
||
|
// Don't touch logon session flag, unless we're
|
||
|
// dealing w/ our own logon session. This means
|
||
|
// we don't have a TGT for our initial logon session,
|
||
|
// See RefreshTgt() -- only place we supply logon session
|
||
|
//
|
||
|
if (PrimaryLogonSessionCredential)
|
||
|
{
|
||
|
LocalLogonSession->LogonSessionFlags |= KERB_LOGON_DEFERRED;
|
||
|
D_DebugLog((DEB_TRACE_CRED, "Toggling ON logon deferred bit for logon session %x\n", LogonSession));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Or, we expected it to be there with our (supplied) credential
|
||
|
//
|
||
|
if (ARGUMENT_PRESENT(Credential))
|
||
|
{
|
||
|
Credential->CredentialFlags &= ~KERB_CRED_TGT_AVAIL;
|
||
|
}
|
||
|
|
||
|
KerbUnlockLogonSessions(LocalLogonSession);
|
||
|
}
|
||
|
|
||
|
if (!ARGUMENT_PRESENT(LogonSession) && (LocalLogonSession != NULL))
|
||
|
{
|
||
|
KerbDereferenceLogonSession(LocalLogonSession);
|
||
|
}
|
||
|
if (Tgt != NULL)
|
||
|
{
|
||
|
KerbDereferenceTicketCacheEntry(
|
||
|
Tgt
|
||
|
);
|
||
|
|
||
|
}
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbReferenceCredential
|
||
|
//
|
||
|
// Synopsis: Locates a logon session from the logon ID and references it
|
||
|
//
|
||
|
// Effects: Increments reference count and possible unlinks it from list
|
||
|
//
|
||
|
// Arguments: LogonId - LogonId of logon session to locate
|
||
|
// RequiredFlags - Flags required
|
||
|
// RemoveFromList - If TRUE, logon session will be delinked
|
||
|
// Credential - Receives the referenced credential
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: NT status codes
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
KerbReferenceCredential(
|
||
|
IN LSA_SEC_HANDLE CredentialHandle,
|
||
|
IN ULONG RequiredFlags,
|
||
|
IN BOOLEAN RemoveFromList,
|
||
|
OUT PKERB_CREDENTIAL * Credential
|
||
|
)
|
||
|
{
|
||
|
PKERB_CREDENTIAL LocalCredential = NULL;
|
||
|
BOOLEAN Found = FALSE;
|
||
|
SECPKG_CALL_INFO CallInfo;
|
||
|
BOOLEAN LocalRemoveFromList = FALSE;
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
|
||
|
ULONG DereferenceCount;
|
||
|
|
||
|
*Credential = NULL;
|
||
|
|
||
|
if(LsaFunctions->GetCallInfo(&CallInfo))
|
||
|
{
|
||
|
DereferenceCount = CallInfo.CallCount;
|
||
|
} else {
|
||
|
ASSERT((STATUS_INTERNAL_ERROR == STATUS_SUCCESS));
|
||
|
return STATUS_INTERNAL_ERROR;
|
||
|
}
|
||
|
|
||
|
if( CallInfo.Attributes & SECPKG_CALL_CLEANUP )
|
||
|
{
|
||
|
CallInfo.Attributes |= SECPKG_CALL_IS_TCB;
|
||
|
DebugLog((DEB_TRACE, "CredHandle %p leaked by ProcessId %x Deref count: %x\n",
|
||
|
CredentialHandle, CallInfo.ProcessId, DereferenceCount));
|
||
|
}
|
||
|
|
||
|
KerbLockList(&KerbCredentialList);
|
||
|
|
||
|
//
|
||
|
// Go through the list of logon sessions looking for the correct
|
||
|
// LUID
|
||
|
//
|
||
|
|
||
|
__try {
|
||
|
LocalCredential = (PKERB_CREDENTIAL)CredentialHandle;
|
||
|
|
||
|
while( LocalCredential->CredentialTag == KERB_CREDENTIAL_TAG_ACTIVE )
|
||
|
{
|
||
|
if (((CallInfo.Attributes & SECPKG_CALL_IS_TCB) == 0) &&
|
||
|
(LocalCredential->ClientProcess != CallInfo.ProcessId) )
|
||
|
{
|
||
|
D_DebugLog((DEB_ERROR,"Trying to reference a credential from another process! %ws, line %d\n", THIS_FILE, __LINE__));
|
||
|
// FESTER
|
||
|
D_DebugLog((DEB_ERROR, "Cred - %x \nClient process - %d Call info Pid - %d\n", LocalCredential, LocalCredential->ClientProcess, CallInfo.ProcessId));
|
||
|
Found = FALSE;
|
||
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
KerbReferenceListEntry(
|
||
|
&KerbCredentialList,
|
||
|
&LocalCredential->ListEntry,
|
||
|
FALSE // don't remove
|
||
|
);
|
||
|
|
||
|
Found = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
} __except (EXCEPTION_EXECUTE_HANDLER)
|
||
|
{
|
||
|
D_DebugLog((DEB_ERROR,"Trying to reference invalid credential %ws, line %d\n", THIS_FILE, __LINE__));
|
||
|
Found = FALSE;
|
||
|
}
|
||
|
|
||
|
if (!Found)
|
||
|
{
|
||
|
LocalCredential = NULL;
|
||
|
Status = STATUS_INVALID_HANDLE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ULONG MissingFlags = RequiredFlags - (LocalCredential->CredentialFlags & RequiredFlags);
|
||
|
|
||
|
if (MissingFlags != 0)
|
||
|
{
|
||
|
D_DebugLog((DEB_TRACE,"Credential %p is missing flags: needs %x\n",
|
||
|
Credential,
|
||
|
MissingFlags));
|
||
|
|
||
|
if ((MissingFlags &= KERB_CRED_TGT_AVAIL) != 0)
|
||
|
{
|
||
|
//
|
||
|
// if this is a cred for a local account then no point in attempting to
|
||
|
// get a TGT
|
||
|
//
|
||
|
if ((LocalCredential->CredentialFlags & KERB_CRED_LOCAL_ACCOUNT) == 0)
|
||
|
{
|
||
|
DsysAssert(!RemoveFromList);
|
||
|
|
||
|
KerbUnlockList(&KerbCredentialList);
|
||
|
|
||
|
D_DebugLog((DEB_TRACE_CRED,"Getting missing TGT for credential %x\n", LocalCredential));
|
||
|
|
||
|
Status = KerbGetTicketForCredential(
|
||
|
NULL,
|
||
|
LocalCredential,
|
||
|
NULL,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
KerbLockList(&KerbCredentialList);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Status = SEC_E_NO_CREDENTIALS;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
|
||
|
|
||
|
//
|
||
|
// Since there may be multiple outstanding handles using this
|
||
|
// structure we don't want to really remove it from the list unless
|
||
|
// the last one releases it.
|
||
|
//
|
||
|
|
||
|
if (RemoveFromList)
|
||
|
{
|
||
|
ASSERT( DereferenceCount != 0 );
|
||
|
ASSERT ( (DereferenceCount <= LocalCredential->HandleCount) );
|
||
|
|
||
|
if( DereferenceCount > LocalCredential->HandleCount ) {
|
||
|
LocalCredential->HandleCount = 0;
|
||
|
} else {
|
||
|
LocalCredential->HandleCount -= DereferenceCount;
|
||
|
}
|
||
|
|
||
|
if (LocalCredential->HandleCount == 0)
|
||
|
{
|
||
|
LocalRemoveFromList = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KerbReferenceListEntry(
|
||
|
&KerbCredentialList,
|
||
|
&LocalCredential->ListEntry,
|
||
|
LocalRemoveFromList
|
||
|
);
|
||
|
|
||
|
KerbDereferenceCredential(LocalCredential);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Remove the earlier reference
|
||
|
//
|
||
|
|
||
|
KerbDereferenceCredential(LocalCredential);
|
||
|
LocalCredential = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
*Credential = LocalCredential;
|
||
|
}
|
||
|
|
||
|
KerbUnlockList(&KerbCredentialList);
|
||
|
return(Status);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbDereferenceCredential
|
||
|
//
|
||
|
// Synopsis: Dereferences a logon session - if reference count goes
|
||
|
// to zero it frees the logon session
|
||
|
//
|
||
|
// Effects: decrements reference count
|
||
|
//
|
||
|
// Arguments: Credential - Logon session to dereference
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: none
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
VOID
|
||
|
KerbDereferenceCredential(
|
||
|
IN PKERB_CREDENTIAL Credential
|
||
|
)
|
||
|
{
|
||
|
if (KerbDereferenceListEntry(
|
||
|
&Credential->ListEntry,
|
||
|
&KerbCredentialList
|
||
|
) )
|
||
|
{
|
||
|
KerbFreeCredential(Credential);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbPurgeCredentials
|
||
|
//
|
||
|
// Synopsis: Purges the list of credentials associated with a logon session
|
||
|
// by dereferencing and unlinking them.
|
||
|
//
|
||
|
// Effects: Unlinks all credential on the list
|
||
|
//
|
||
|
// Arguments: CredentialList - List of credentials to purge
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: none
|
||
|
//
|
||
|
// Notes: No longer used, as some system processes hold cred handles long
|
||
|
// after logons go away. Leads to refcounting disasters.
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
/*
|
||
|
|
||
|
VOID
|
||
|
KerbPurgeCredentials(
|
||
|
IN PLIST_ENTRY CredentialList
|
||
|
)
|
||
|
{
|
||
|
PKERB_CREDENTIAL Credential;
|
||
|
|
||
|
KerbLockList(&KerbCredentialList);
|
||
|
while (!IsListEmpty(CredentialList))
|
||
|
{
|
||
|
Credential = CONTAINING_RECORD(
|
||
|
CredentialList->Flink,
|
||
|
KERB_CREDENTIAL,
|
||
|
NextForThisLogonSession
|
||
|
);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Remove it from the credential list
|
||
|
//
|
||
|
|
||
|
//RemoveEntryList(&Credential->NextForThisLogonSession);
|
||
|
Credential->HandleCount = 0;
|
||
|
|
||
|
//
|
||
|
// Reference it to unlink it and then dereference it
|
||
|
//
|
||
|
|
||
|
KerbReferenceListEntry(
|
||
|
&KerbCredentialList,
|
||
|
&Credential->ListEntry,
|
||
|
TRUE
|
||
|
);
|
||
|
|
||
|
KerbDereferenceCredential(Credential);
|
||
|
|
||
|
}
|
||
|
KerbUnlockList(&KerbCredentialList);
|
||
|
|
||
|
} */
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbLocateCredential
|
||
|
//
|
||
|
// Synopsis:
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
PKERB_CREDENTIAL
|
||
|
KerbLocateCredential(
|
||
|
IN PLUID LogonId,
|
||
|
IN ULONG CredentialUseFlags,
|
||
|
IN PKERB_PRIMARY_CREDENTIAL SuppliedCredentials,
|
||
|
IN ULONG CredentialFlags,
|
||
|
IN PUNICODE_STRING CredentialName
|
||
|
)
|
||
|
{
|
||
|
PLIST_ENTRY ListEntry;
|
||
|
PKERB_CREDENTIAL Credential = NULL;
|
||
|
BOOLEAN Found = FALSE;
|
||
|
SECPKG_CALL_INFO CallInfo;
|
||
|
NT_OWF_PASSWORD HashPassword;
|
||
|
|
||
|
if( SuppliedCredentials != NULL )
|
||
|
{
|
||
|
if( SuppliedCredentials->ClearPassword.Buffer != NULL )
|
||
|
{
|
||
|
RtlCalculateNtOwfPassword(
|
||
|
&SuppliedCredentials->ClearPassword,
|
||
|
&HashPassword
|
||
|
);
|
||
|
} else {
|
||
|
ZeroMemory( &HashPassword, sizeof(HashPassword) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Match both flags
|
||
|
//
|
||
|
|
||
|
CredentialUseFlags |= CredentialFlags;
|
||
|
|
||
|
if(!LsaFunctions->GetCallInfo(&CallInfo))
|
||
|
{
|
||
|
|
||
|
D_DebugLog((DEB_ERROR,"Failed to get client info:. %ws, line %d\n",
|
||
|
THIS_FILE, __LINE__));
|
||
|
return(NULL);
|
||
|
}
|
||
|
|
||
|
|
||
|
KerbLockList(&KerbCredentialList);
|
||
|
|
||
|
//
|
||
|
// Go through the list of logon sessions looking for the correct
|
||
|
// LUID
|
||
|
//
|
||
|
|
||
|
for (ListEntry = KerbCredentialList.List.Flink ;
|
||
|
ListEntry != &KerbCredentialList.List ;
|
||
|
ListEntry = ListEntry->Flink )
|
||
|
{
|
||
|
Credential = CONTAINING_RECORD(ListEntry, KERB_CREDENTIAL, ListEntry.Next);
|
||
|
|
||
|
if( (Credential->ClientProcess != CallInfo.ProcessId) )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( (Credential->CredentialFlags & KERB_CRED_MATCH_FLAGS) != CredentialUseFlags)
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if(!RtlEqualLuid(
|
||
|
&Credential->LogonId,
|
||
|
LogonId
|
||
|
))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if(!RtlEqualUnicodeString(
|
||
|
CredentialName,
|
||
|
&Credential->CredentialName,
|
||
|
FALSE
|
||
|
))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if( SuppliedCredentials != NULL )
|
||
|
{
|
||
|
//
|
||
|
// credentials supplied, but candidate didn't have creds. continue search.
|
||
|
//
|
||
|
|
||
|
if( Credential->SuppliedCredentials == NULL )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
|
||
|
if(!RtlEqualUnicodeString(
|
||
|
&SuppliedCredentials->UserName,
|
||
|
&Credential->SuppliedCredentials->UserName,
|
||
|
FALSE
|
||
|
))
|
||
|
{
|
||
|
if(!RtlEqualUnicodeString(
|
||
|
&SuppliedCredentials->UserName,
|
||
|
&Credential->SuppliedCredentials->OldUserName,
|
||
|
FALSE
|
||
|
))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// note: both candidate and input SuppliedCredentials ClearPassword
|
||
|
// is actually encrypted via KerbHidePassword().
|
||
|
//
|
||
|
|
||
|
if(!RtlEqualMemory(
|
||
|
&HashPassword,
|
||
|
&Credential->SuppliedCredentials->OldHashPassword,
|
||
|
sizeof(HashPassword)
|
||
|
))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// optimize for UPN case:
|
||
|
// check as typed versus as stored/updated first,
|
||
|
// then check as typed versus as typed in original cred build.
|
||
|
//
|
||
|
|
||
|
if(!RtlEqualUnicodeString(
|
||
|
&SuppliedCredentials->DomainName,
|
||
|
&Credential->SuppliedCredentials->DomainName,
|
||
|
FALSE
|
||
|
))
|
||
|
{
|
||
|
if(!RtlEqualUnicodeString(
|
||
|
&SuppliedCredentials->DomainName,
|
||
|
&Credential->SuppliedCredentials->OldDomainName,
|
||
|
FALSE
|
||
|
))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if ((SuppliedCredentials->PublicKeyCreds != NULL) &&
|
||
|
(Credential->SuppliedCredentials->PublicKeyCreds != NULL))
|
||
|
{
|
||
|
|
||
|
if(!RtlEqualUnicodeString(
|
||
|
&SuppliedCredentials->PublicKeyCreds->Pin,
|
||
|
&Credential->SuppliedCredentials->PublicKeyCreds->Pin,
|
||
|
FALSE
|
||
|
))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!KerbComparePublicKeyCreds(
|
||
|
SuppliedCredentials->PublicKeyCreds,
|
||
|
Credential->SuppliedCredentials->PublicKeyCreds
|
||
|
))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
} else {
|
||
|
|
||
|
//
|
||
|
// credentials not supplied, but candidate has creds. continue search
|
||
|
//
|
||
|
|
||
|
if( Credential->SuppliedCredentials != NULL )
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
KerbReferenceListEntry(
|
||
|
&KerbCredentialList,
|
||
|
&Credential->ListEntry,
|
||
|
FALSE
|
||
|
);
|
||
|
|
||
|
Credential->HandleCount++;
|
||
|
Found = TRUE;
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
KerbUnlockList(&KerbCredentialList);
|
||
|
|
||
|
if (!Found)
|
||
|
{
|
||
|
Credential = NULL;
|
||
|
}
|
||
|
return(Credential);
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbCreateCredential
|
||
|
//
|
||
|
// Synopsis: Creates a new credential and links it to the credential list
|
||
|
// and the list for this logon session
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: LogonId - LogonId for this logon session
|
||
|
// LogonSession - LogonSession for the client
|
||
|
// CredentialUseFlags - Flags indicating if the credential is
|
||
|
// inbound or outbound
|
||
|
// SuppliedCredentials - (Optionally) supplied credentials to store
|
||
|
// in the credentials. If these are present, there need
|
||
|
// not be a password on the logon session. The field is
|
||
|
// zeroed when the primary creds are stuck in the
|
||
|
// credential structure.
|
||
|
// CredentialFlags - Flags about how credentials are to be
|
||
|
// used, such as to not use a PAC or to use a null
|
||
|
// session.
|
||
|
// NewCredential - Receives new credential, referenced and linked
|
||
|
// ExpirationTime - Receives credential expiration time
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: STATUS_SUCCESS on success,
|
||
|
// STATUS_INSUFFICIENT_RESOURCES on allocation failure
|
||
|
//
|
||
|
// Notes: Readers and writers of this credential must hold the
|
||
|
// credential lock
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
NTSTATUS
|
||
|
KerbCreateCredential(
|
||
|
IN PLUID LogonId,
|
||
|
IN PKERB_LOGON_SESSION LogonSession,
|
||
|
IN ULONG CredentialUseFlags,
|
||
|
IN PKERB_PRIMARY_CREDENTIAL * SuppliedCredentials,
|
||
|
IN ULONG CredentialFlags,
|
||
|
IN PUNICODE_STRING CredentialName,
|
||
|
OUT PKERB_CREDENTIAL * NewCredential,
|
||
|
OUT PTimeStamp ExpirationTime
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status;
|
||
|
PKERB_CREDENTIAL Credential = NULL;
|
||
|
ULONG LogonSessionFlags = 0;
|
||
|
UNICODE_STRING ServiceRealm = NULL_UNICODE_STRING;
|
||
|
BOOLEAN FoundKdc = FALSE;
|
||
|
BOOLEAN fLocalCredential = FALSE;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Check to see if we already have a credential for this situation
|
||
|
//
|
||
|
|
||
|
Credential = KerbLocateCredential(
|
||
|
LogonId,
|
||
|
CredentialUseFlags,
|
||
|
*SuppliedCredentials,
|
||
|
CredentialFlags,
|
||
|
CredentialName
|
||
|
);
|
||
|
if (Credential != NULL)
|
||
|
{
|
||
|
KerbReadLockLogonSessions(LogonSession);
|
||
|
|
||
|
*ExpirationTime = LogonSession->Lifetime;
|
||
|
|
||
|
KerbUnlockLogonSessions(LogonSession);
|
||
|
|
||
|
*NewCredential = Credential;
|
||
|
return(STATUS_SUCCESS);
|
||
|
}
|
||
|
|
||
|
Status = KerbAllocateCredential(&Credential);
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
Credential->LogonId = *LogonId;
|
||
|
|
||
|
//
|
||
|
// Make sure the flags are valid
|
||
|
//
|
||
|
|
||
|
if ((CredentialUseFlags & ~SECPKG_CRED_BOTH) != 0)
|
||
|
{
|
||
|
D_DebugLog((DEB_ERROR,"Invalid credential use flags: 0x%x. %ws, line %d\n",CredentialUseFlags, THIS_FILE, __LINE__));
|
||
|
Status = STATUS_INVALID_PARAMETER;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Make sure the logon session is valid for acquiring credentials
|
||
|
//
|
||
|
|
||
|
KerbReadLockLogonSessions(LogonSession);
|
||
|
|
||
|
LogonSessionFlags = LogonSession->LogonSessionFlags;
|
||
|
*ExpirationTime = LogonSession->Lifetime;
|
||
|
|
||
|
KerbUnlockLogonSessions(LogonSession);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Make sure the logon session is not local only.
|
||
|
// We do not handle the local only case.
|
||
|
//
|
||
|
if (((LogonSessionFlags & KERB_LOGON_LOCAL_ONLY) != 0) &&
|
||
|
(*SuppliedCredentials == NULL ))
|
||
|
{
|
||
|
D_DebugLog((DEB_WARN, "Trying to acquire cred handle for local logon session\n"));
|
||
|
fLocalCredential = TRUE;
|
||
|
/*
|
||
|
Status = SEC_E_NO_CREDENTIALS;
|
||
|
goto Cleanup;
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
Credential->SuppliedCredentials = *SuppliedCredentials;
|
||
|
*SuppliedCredentials = NULL;
|
||
|
|
||
|
Credential->CredentialName = *CredentialName;
|
||
|
CredentialName->Buffer = NULL;
|
||
|
|
||
|
Credential->CredentialFlags = CredentialUseFlags | CredentialFlags;
|
||
|
if (fLocalCredential)
|
||
|
{
|
||
|
Credential->CredentialFlags |= KERB_CRED_LOCAL_ACCOUNT;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we have no password, we must either not have deferred the logon
|
||
|
// and we can't do inbound with no TGT.
|
||
|
//
|
||
|
|
||
|
if (((LogonSessionFlags & KERB_LOGON_NO_PASSWORD) != 0) &&
|
||
|
((LogonSessionFlags & KERB_LOGON_SMARTCARD) == 0) &&
|
||
|
(Credential->SuppliedCredentials == NULL))
|
||
|
{
|
||
|
if (((CredentialUseFlags & SECPKG_CRED_OUTBOUND) != 0) &&
|
||
|
((LogonSessionFlags & KERB_LOGON_DEFERRED) != 0))
|
||
|
{
|
||
|
DebugLog((DEB_WARN,"Trying to acquire cred handle w/ no supplied creds for logon session with no pass or TGT\n"));
|
||
|
Status = SEC_E_NO_CREDENTIALS;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check to see if this was a deferred logon - if so, try to
|
||
|
// finish it now.
|
||
|
//
|
||
|
|
||
|
if (((CredentialFlags & KERB_CRED_NULL_SESSION) == 0) &&
|
||
|
(((LogonSessionFlags & KERB_LOGON_DEFERRED) != 0) ||
|
||
|
(Credential->SuppliedCredentials != NULL)))
|
||
|
|
||
|
{
|
||
|
//
|
||
|
// If this is outbound, we need to get a TGT or supplied credentials
|
||
|
//
|
||
|
|
||
|
if ((CredentialUseFlags & SECPKG_CRED_OUTBOUND) != 0)
|
||
|
{
|
||
|
//
|
||
|
// Get an authentication ticket
|
||
|
//
|
||
|
#ifdef notdef
|
||
|
Status = KerbGetTicketForCredential(
|
||
|
LogonSession,
|
||
|
Credential
|
||
|
);
|
||
|
|
||
|
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR,"Failed to get TGT for credential: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__ ));
|
||
|
goto Cleanup;
|
||
|
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Verify that a KDC actually exists for this users's realm -
|
||
|
// otherwise we don't want to bother with Kerberos. If someone
|
||
|
// is explicitly supplying credentials we probably want to
|
||
|
// allow them to do it.
|
||
|
//
|
||
|
|
||
|
if (((CredentialUseFlags & SECPKG_CRED_INBOUND) != 0) &&
|
||
|
((LogonSessionFlags & KERB_LOGON_DEFERRED) != 0) &&
|
||
|
((CredentialFlags & KERB_CRED_NULL_SESSION) == 0) &&
|
||
|
(Credential->SuppliedCredentials == NULL))
|
||
|
{
|
||
|
|
||
|
if ((LogonSessionFlags & KERB_LOGON_NO_PASSWORD) != 0)
|
||
|
{
|
||
|
D_DebugLog((DEB_WARN,"Trying to get inbound cred with no pwd or tgt\n"));
|
||
|
Status = SEC_E_NO_CREDENTIALS;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
KerbReadLockLogonSessions(LogonSession);
|
||
|
|
||
|
Status = KerbDuplicateString(
|
||
|
&ServiceRealm,
|
||
|
&LogonSession->PrimaryCredentials.DomainName
|
||
|
);
|
||
|
KerbUnlockLogonSessions(LogonSession);
|
||
|
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If there was no domain name, then assume there is a KDC.
|
||
|
//
|
||
|
|
||
|
if (ServiceRealm.Length == 0)
|
||
|
{
|
||
|
FoundKdc = TRUE;
|
||
|
}
|
||
|
|
||
|
if (!FoundKdc)
|
||
|
{
|
||
|
//
|
||
|
// If we are a DC, or f this domain is our worksatation
|
||
|
// domain, check to see if
|
||
|
// we have a DNS name. If we do, then at one point we were
|
||
|
// part of an NT5 domain. Otherwise call DsGetDCName to see
|
||
|
// if there is a KDC around
|
||
|
//
|
||
|
|
||
|
if ((KerbGlobalRole == KerbRoleDomainController) ||
|
||
|
KerbIsThisOurDomain(
|
||
|
&ServiceRealm
|
||
|
))
|
||
|
{
|
||
|
FoundKdc = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we haven't found one yet, try looking for a KDC in
|
||
|
// this domain
|
||
|
//
|
||
|
|
||
|
if (!FoundKdc)
|
||
|
{
|
||
|
PKERB_BINDING_CACHE_ENTRY BindingHandle = NULL;
|
||
|
|
||
|
DsysAssert(ServiceRealm.MaximumLength >= ServiceRealm.Length + sizeof(WCHAR));
|
||
|
DsysAssert(ServiceRealm.Buffer[ServiceRealm.Length/sizeof(WCHAR)] == L'\0');
|
||
|
|
||
|
Status = KerbGetKdcBinding(
|
||
|
&ServiceRealm,
|
||
|
NULL, // no account name
|
||
|
0, // no desired flags,
|
||
|
FALSE, // don't call kadmin
|
||
|
FALSE,
|
||
|
&BindingHandle
|
||
|
);
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
FoundKdc = TRUE;
|
||
|
KerbDereferenceBindingCacheEntry(BindingHandle);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
if (!FoundKdc)
|
||
|
{
|
||
|
D_DebugLog((DEB_ERROR,"Didn't find KDC for domain %wZ. %ws, line %d\n",
|
||
|
&ServiceRealm, THIS_FILE, __LINE__ ));
|
||
|
Status = SEC_E_NO_AUTHENTICATING_AUTHORITY;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Credential->CredentialFlags |= KERB_CRED_TGT_AVAIL;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Insert the credential into the list of credentials
|
||
|
//
|
||
|
|
||
|
KerbInsertCredential(Credential);
|
||
|
|
||
|
//
|
||
|
// Notice: the order of acquiring these locks is important.
|
||
|
//
|
||
|
|
||
|
*NewCredential = Credential;
|
||
|
|
||
|
Cleanup:
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
if (Credential != NULL)
|
||
|
{
|
||
|
//
|
||
|
// Make sure we haven't linked this one yet.
|
||
|
//
|
||
|
|
||
|
DsysAssert(Credential->ListEntry.ReferenceCount == 1);
|
||
|
KerbFreeCredential(Credential);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Map the error if necessary. Normally STATUS_OBJECT_NAME_NOT_FOUND
|
||
|
// gets mapped to SEC_E_UNKNOWN_TARGET, but this is an invalid
|
||
|
// status to return from AcquireCredentialsHandle, so instead
|
||
|
// return SEC_E_NO_CREDENTIALS.
|
||
|
//
|
||
|
|
||
|
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
|
||
|
{
|
||
|
Status = SEC_E_NO_CREDENTIALS;
|
||
|
}
|
||
|
}
|
||
|
KerbFreeString(&ServiceRealm);
|
||
|
return(Status);
|
||
|
}
|