|
|
//+-----------------------------------------------------------------------
//
// 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, CRED_MGR_LOCK_ENUM ); 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, FALSE); 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
// f
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 { //
// get TGTs in order to normalize supplied UPN credentials for non TCB callers
//
if ((RequiredFlags & KERB_CRED_INBOUND) && LocalCredential->SuppliedCredentials && (LocalCredential->SuppliedCredentials->DomainName.Length == 0) && ((CallInfo.Attributes & SECPKG_CALL_IS_TCB) == 0)) { DebugLog((DEB_TRACE_CRED, "KerbReferenceCredential trying to normalize UPN %wZ, RequiredFlags %#x\n", &LocalCredential->SuppliedCredentials->UserName, RequiredFlags)); RequiredFlags |= KERB_CRED_TGT_AVAIL; }
//
// In some cases, we need to get a TGT here - but not for the S4U cases.
//
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 (( LocalCredential->CredentialFlags & (KERB_CRED_S4U_REQUIRED | 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)) {
//
// note: both candidate and input SuppliedCredentials Pin
// is actually encrypted via KerbHidePassword().
//
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;
//
// Make sure the flags are valid
//
if (( CredentialUseFlags == 0) || ((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; }
//
// 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 logon session is valid for acquiring credentials
//
KerbReadLockLogonSessions(LogonSession);
LogonSessionFlags = LogonSession->LogonSessionFlags; *ExpirationTime = LogonSession->Lifetime;
KerbUnlockLogonSessions(LogonSession);
Credential->SuppliedCredentials = *SuppliedCredentials; *SuppliedCredentials = NULL;
Credential->CredentialName = *CredentialName; CredentialName->Buffer = NULL;
Credential->CredentialFlags = CredentialUseFlags | CredentialFlags;
if (( CredentialUseFlags & KERB_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; }
//
// Evaluate the outbound credential cases. We may need to do S4U.
//
if (( Credential->SuppliedCredentials == NULL ) && (( CredentialFlags & KERB_CRED_NULL_SESSION) == 0)) { if (( CredentialUseFlags & KERB_CRED_OUTBOUND) != 0) { if (( LogonSessionFlags & KERB_LOGON_S4U_REQUIRED ) != 0) { //
// This logon session was created w/o the capabilities of
// going "off box". In this case, we need to fail this transaction
//
if (( LogonSessionFlags & KERB_LOGON_DELEGATE_OK ) == 0) { DebugLog((DEB_TRACE_CRED, "Cant go off box w/ non-fwdble logon session & no supp creds\n")); Status = SEC_E_NO_CREDENTIALS; goto Cleanup; } else { //
// All others are OK for leaving the machine. Mark this cred
// as an S4U cred, then get the heck out of here.
//
DebugLog((DEB_TRACE_CRED, "Acquiring cred, S4U required\n")); Credential->CredentialFlags |= KERB_CRED_S4U_REQUIRED; } } else if (( LogonSessionFlags & KERB_LOGON_LOCAL_ONLY ) != 0) { //
// Local logon session - we can only use these w/o supplied creds
// if we use credman.
//
Credential->CredentialFlags |= KERB_CRED_LOCAL_ACCOUNT;
} else if (( LogonSessionFlags & KERB_LOGON_DEFERRED ) != 0) { //
// I don't believe we'll hit this case anymore, as all "non pwd" logon sessions
// should be using S4U, unless they're local.
//
if (( LogonSessionFlags & KERB_LOGON_NO_PASSWORD ) != 0 ) { DebugLog((DEB_TRACE_CRED,"Trying to acquire cred handle w/ no supplied creds for ls (%p) no pass or TGT\n", LogonSession)); Status = SEC_E_NO_CREDENTIALS; goto Cleanup; }
} else if (( LogonSessionFlags & KERB_LOGON_DEFERRED ) == 0) { //
// Normal case, where we have a TGT
//
Credential->CredentialFlags |= KERB_CRED_TGT_AVAIL; } else { //
// Generic case, where we don't have a TGT for a domain logon,
// but we have info for a TGT. Assert until we verify we're not missing
// anything important - Contact Todds
//
DebugLog((DEB_ERROR, "Missing case for session FLAGs %x\n", LogonSessionFlags)); } }
if (( CredentialUseFlags & KERB_CRED_INBOUND) != 0) { if (( LogonSessionFlags & KERB_LOGON_DEFERRED ) == 0) { Credential->CredentialFlags |= KERB_CRED_TGT_AVAIL; } else { if ((LogonSessionFlags & (KERB_LOGON_NO_PASSWORD | KERB_LOGON_LOCAL_ONLY)) != 0) { D_DebugLog((DEB_WARN, "Trying to get inbound cred with no supplied creds, no pwd or tgt, or local only\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 { //
// In this case, we have supplied credentials.
//
D_DebugLog((DEB_TRACE, "Got supplied credentials\n")); }
//
// 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); }
|