|
|
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1996
//
// File: miscapi.cxx
//
// Contents: Code for miscellaneous lsa mode Kerberos entrypoints
//
//
// History: 16-April-1996 MikeSw Created
//
//------------------------------------------------------------------------
#include <kerb.hxx>
#include <kerbp.h>
#include <crypt.h> // NT_OWF_PASSWORD_LENGTH
#include <kerbpass.h>
#include <spncache.h>
#ifdef RETAIL_LOG_SUPPORT
static TCHAR THIS_FILE[]=TEXT(__FILE__); #endif
//
// LsaApCallPackage() function dispatch table
//
NTSTATUS NTAPI KerbDebugRequest( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbQueryTicketCache( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbQueryTicketCacheEx( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbChangeMachinePassword( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbVerifyPac( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbRetrieveTicket( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbSetIpAddresses( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbPurgeTicket( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbPurgeTicketEx( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbRetrieveEncodedTicket( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbRetrieveEncodedTicketEx( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbAddBindingCacheEntry( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbDecryptMessage( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbVerifyCredentials( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbRefreshSmartcardCredentials( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbAddExtraCredential( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
NTSTATUS NTAPI KerbTicketAsRequestSafe( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus );
PLSA_AP_CALL_PACKAGE KerbCallPackageDispatch[] = { #if DBG
KerbDebugRequest, #else
NULL, #endif
KerbQueryTicketCache, KerbChangeMachinePassword, KerbVerifyPac, KerbRetrieveTicket, KerbSetIpAddresses, KerbPurgeTicket, KerbChangePassword, KerbRetrieveEncodedTicket, #if DBG
KerbDecryptMessage, #else
NULL, #endif
KerbAddBindingCacheEntry, KerbSetPassword, KerbSetPassword, KerbVerifyCredentials, KerbQueryTicketCacheEx, KerbPurgeTicketEx, // KerbRetrieveEncodedTicketEx,
KerbRefreshSmartcardCredentials, KerbAddExtraCredential, NULL, KerbTicketAsRequestSafe, };
//+-------------------------------------------------------------------------
//
// Function: SpGetUserInfo
//
// Synopsis: Gets information about a user
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI SpGetUserInfo( IN PLUID LogonId, IN ULONG Flags, OUT PSecurityUserData * UserData ) { return(STATUS_NOT_SUPPORTED); }
//+-------------------------------------------------------------------------
//
// Function: LsaApCallPackage
//
// Synopsis: Kerberos entrypoint for LsaCallAuthenticationPackage
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI LsaApCallPackage( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; ULONG MessageType; PLSA_AP_CALL_PACKAGE TempFn = NULL;
//
// Get the messsage type from the protocol submit buffer.
//
if ( SubmitBufferLength < sizeof(KERB_PROTOCOL_MESSAGE_TYPE) ) { return STATUS_INVALID_PARAMETER; }
MessageType = (ULONG) *((PKERB_PROTOCOL_MESSAGE_TYPE)(ProtocolSubmitBuffer));
if ((MessageType >= (sizeof(KerbCallPackageDispatch)/sizeof(KerbCallPackageDispatch[0]))) || (KerbCallPackageDispatch[MessageType] == NULL)) {
return STATUS_INVALID_PARAMETER; }
//
// Allow the dispatch routines to only set the return buffer information
// on success conditions.
//
*ProtocolReturnBuffer = NULL; *ReturnBufferLength = 0;
//
// Call the appropriate routine for this message.
//
TempFn = KerbCallPackageDispatch[MessageType];
D_DebugLog((DEB_WARN, "LsaApCallPackage %#x(%d), TempFn %p\n", MessageType, MessageType, TempFn));
if (!TempFn) { Status = STATUS_NOT_SUPPORTED; goto Cleanup; }
Status = (*TempFn)( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus );
// RtlCheckForOrphanedCriticalSections(NtCurrentThread());
Cleanup: return(Status);
}
NTSTATUS NTAPI LsaApCallPackageUntrusted( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { KERB_PROTOCOL_MESSAGE_TYPE MessageType;
//
// Get the messsage type from the protocol submit buffer.
//
if ( SubmitBufferLength < sizeof(KERB_PROTOCOL_MESSAGE_TYPE) ) { return STATUS_INVALID_PARAMETER; }
MessageType = *((PKERB_PROTOCOL_MESSAGE_TYPE)(ProtocolSubmitBuffer));
if ( MessageType >= (sizeof(KerbCallPackageDispatch)/sizeof(KerbCallPackageDispatch[0]))) { return STATUS_INVALID_PARAMETER; }
//
// Untrusted clients are not allowed to call the ChangeMachinePassword function
//
if (MessageType == KerbChangeMachinePasswordMessage) { return STATUS_ACCESS_DENIED; }
//
// Allow the dispatch routines to only set the return buffer information
// on success conditions.
//
*ProtocolReturnBuffer = NULL; *ReturnBufferLength = 0;
//
// Call the appropriate routine for this message.
//
return(LsaApCallPackage( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus) ); }
NTSTATUS NTAPI LsaApCallPackagePassthrough( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { KERB_PROTOCOL_MESSAGE_TYPE MessageType;
//
// Get the messsage type from the protocol submit buffer.
//
if ( SubmitBufferLength < sizeof(KERB_PROTOCOL_MESSAGE_TYPE) ) { return STATUS_INVALID_PARAMETER; }
MessageType = *((PKERB_PROTOCOL_MESSAGE_TYPE)(ProtocolSubmitBuffer));
if ( MessageType >= (sizeof(KerbCallPackageDispatch)/sizeof(KerbCallPackageDispatch[0]))) { return STATUS_INVALID_PARAMETER; }
//
// only allow passthrough related requests.
//
if (MessageType != KerbVerifyPacMessage) { return STATUS_ACCESS_DENIED; }
//
// Allow the dispatch routines to only set the return buffer information
// on success conditions.
//
*ProtocolReturnBuffer = NULL; *ReturnBufferLength = 0;
//
// Call the appropriate routine for this message.
//
return(LsaApCallPackage( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus) ); }
//+-------------------------------------------------------------------------
//
// Function: KerbDebugRequest
//
// Synopsis: CallPackage entrypoint for debugging
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbDebugRequest( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status = STATUS_SUCCESS;
#if DBG
PVOID Handle = NULL; PBYTE AuthData = NULL; UNICODE_STRING AccountName = {0};
BYTE Buffer[sizeof(KERB_DEBUG_REPLY) + sizeof(KERB_DEBUG_STATS) - sizeof(UCHAR) * ANYSIZE_ARRAY]; PKERB_DEBUG_REQUEST DebugRequest; PKERB_DEBUG_REPLY DebugReply = (PKERB_DEBUG_REPLY) Buffer; PKERB_DEBUG_STATS DebugStats = (PKERB_DEBUG_STATS) DebugReply->Data;
if (SubmitBufferLength < sizeof(*DebugRequest)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
DebugRequest = (PKERB_DEBUG_REQUEST) ProtocolSubmitBuffer; switch(DebugRequest->DebugRequest) { case KERB_DEBUG_REQ_BREAKPOINT: DbgBreakPoint(); break;
case KERB_DEBUG_REQ_STATISTICS: DebugReply->MessageType = KerbDebugRequestMessage; DebugStats->CacheHits = KerbTicketCacheHits; DebugStats->CacheMisses = KerbTicketCacheMisses; DebugStats->SkewedRequests = KerbSkewState.SkewedRequests; DebugStats->SuccessRequests = KerbSkewState.SuccessRequests; DebugStats->LastSync = KerbSkewState.LastSync; Status = LsaFunctions->AllocateClientBuffer( NULL, sizeof(Buffer), ProtocolReturnBuffer ); if (!NT_SUCCESS(Status)) { goto Cleanup; } Status = LsaFunctions->CopyToClientBuffer( NULL, sizeof(Buffer), *ProtocolReturnBuffer, DebugReply ); if (!NT_SUCCESS(Status)) { LsaFunctions->FreeClientBuffer( NULL, *ProtocolReturnBuffer ); *ProtocolReturnBuffer = NULL; } else { *ReturnBufferLength = sizeof(Buffer); }
break;
case KERB_DEBUG_CREATE_TOKEN: { UNICODE_STRING String, String2; ULONG AuthDataSize = 0; HANDLE TokenHandle = NULL; LUID LogonId; NTSTATUS SubStatus;
RtlInitUnicodeString( &String, L"Administrator" ); RtlInitUnicodeString( &String2, NULL );
Status = LsaFunctions->OpenSamUser( &String, SecNameSamCompatible, &String2, TRUE, // allow guest
0, // reserved
&Handle ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to open sam user: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; }
Status = LsaFunctions->GetUserAuthData( Handle, &AuthData, &AuthDataSize ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to get auth data: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; }
//
// Now create that token
//
Status = LsaFunctions->ConvertAuthDataToToken( AuthData, AuthDataSize, SecurityImpersonation, &KerberosSource, Network, &String, &TokenHandle, &LogonId, &AccountName, &SubStatus ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to create token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } NtClose(TokenHandle); DebugLog((DEB_ERROR,"Logged on account is %wZ. %ws, line %d\n",&AccountName, THIS_FILE, __LINE__)); break;
}
default: Status = STATUS_INVALID_PARAMETER; }
Cleanup:
if( Handle != NULL ) { LsaFunctions->CloseSamUser( Handle ); }
if( AuthData != NULL ) { LsaFunctions->FreeLsaHeap( AuthData ); }
if( AccountName.Buffer != NULL ) { LsaFunctions->FreeLsaHeap( AccountName.Buffer ); }
#else
Status = STATUS_INVALID_PARAMETER; #endif
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbRefreshSmartcardCredentials
//
// Synopsis: Notifies Kerberos when the smart card credentials need to
// be updated. Basically a workaround for winlogon session
// switching behavior during TS connects / re-connects.
// When this happens, your HPROV goes bad...
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbRefreshSmartcardCredentials( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_REFRESH_SCCRED_REQUEST ChangeRequest; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonId;
if (SubmitBufferLength < sizeof(KERB_REFRESH_SCCRED_REQUEST)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
ChangeRequest = (PKERB_REFRESH_SCCRED_REQUEST) ProtocolSubmitBuffer;
if (ARGUMENT_PRESENT(ProtocolReturnBuffer)) { *ProtocolReturnBuffer = NULL; }
if (ARGUMENT_PRESENT(ReturnBufferLength)) { *ReturnBufferLength = 0; }
//
// If the caller did not provide a logon id, use the caller's logon id.
//
Status = LsaFunctions->GetClientInfo( &ClientInfo ); if ( !NT_SUCCESS( Status )) { goto Cleanup; }
if ( RtlIsZeroLuid( &ChangeRequest->LogonId )) { LogonId = &ClientInfo.LogonId; } else if ( !ClientInfo.HasTcbPrivilege ) { Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup; } else { LogonId = &ChangeRequest->LogonId; }
LogonSession = KerbReferenceLogonSession( LogonId, FALSE );
DsysAssert(LogonSession != NULL); if (LogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
KerbReleasePkCreds( LogonSession, NULL, TRUE // ok for reuse - save PIN and SChelper data
);
Status = STATUS_SUCCESS;
Cleanup: if (LogonSession != NULL) { KerbDereferenceLogonSession(LogonSession); }
*ProtocolStatus = Status;
return(STATUS_SUCCESS); }
//+-------------------------------------------------------------------------
//
// Function: KerbChangeMachinePassword
//
// Synopsis: Notifies Kerberos when the machine password has changed
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbChangeMachinePassword( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status = STATUS_SUCCESS; LUID SystemLogonId = SYSTEM_LUID; PKERB_LOGON_SESSION SystemLogonSession = NULL; PKERB_CHANGE_MACH_PWD_REQUEST ChangeRequest; ULONG StructureSize = sizeof(KERB_CHANGE_MACH_PWD_REQUEST);
if (ARGUMENT_PRESENT(ProtocolReturnBuffer)) { *ProtocolReturnBuffer = NULL; }
if (ARGUMENT_PRESENT(ReturnBufferLength)) { *ReturnBufferLength = 0; }
#if _WIN64
SECPKG_CALL_INFO CallInfo;
if(!LsaFunctions->GetCallInfo(&CallInfo)) { Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { StructureSize = sizeof(KERB_CHANGE_MACH_PWD_REQUEST_WOW64); }
#endif // _WIN64
if (SubmitBufferLength < StructureSize) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
if (!KerbGlobalInitialized) { Status = STATUS_SUCCESS; DsysAssert(FALSE); goto Cleanup; }
ChangeRequest = (PKERB_CHANGE_MACH_PWD_REQUEST) ProtocolSubmitBuffer;
#if _WIN64
KERB_CHANGE_MACH_PWD_REQUEST LocalChangeRequest;
//
// Thunk 32-bit pointers if this is a WOW caller
//
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { PKERB_CHANGE_MACH_PWD_REQUEST_WOW64 ChangeRequestWOW = (PKERB_CHANGE_MACH_PWD_REQUEST_WOW64) ChangeRequest;
LocalChangeRequest.MessageType = ChangeRequest->MessageType;
UNICODE_STRING_FROM_WOW_STRING(&LocalChangeRequest.NewPassword, &ChangeRequestWOW->NewPassword);
UNICODE_STRING_FROM_WOW_STRING(&LocalChangeRequest.OldPassword, &ChangeRequestWOW->OldPassword);
ChangeRequest = &LocalChangeRequest; }
#endif // _WIN64
//
// Find the system logon session.
//
SystemLogonSession = KerbReferenceLogonSession( &SystemLogonId, FALSE // don't unlink
);
DsysAssert(SystemLogonSession != NULL); if (SystemLogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
//
// Calculate the new password list
//
if (ChangeRequest->NewPassword.Buffer != NULL) { //
// If there is an old password, update with that one first so it
// will later get moved to the old password field.
//
KerbWriteLockLogonSessions(SystemLogonSession); if (ChangeRequest->OldPassword.Buffer != NULL) { Status = KerbChangeCredentialsPassword( &SystemLogonSession->PrimaryCredentials, &ChangeRequest->OldPassword, NULL, // no etype info
MachineAccount, PRIMARY_CRED_CLEAR_PASSWORD ); } if (NT_SUCCESS(Status)) { Status = KerbChangeCredentialsPassword( &SystemLogonSession->PrimaryCredentials, &ChangeRequest->NewPassword, NULL, // no etype info
MachineAccount, PRIMARY_CRED_CLEAR_PASSWORD ); }
KerbUnlockLogonSessions(SystemLogonSession);
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Update the flags to indicate that we have a password
//
KerbWriteLockLogonSessions(SystemLogonSession); SystemLogonSession->LogonSessionFlags &= ~(KERB_LOGON_LOCAL_ONLY | KERB_LOGON_NO_PASSWORD); KerbUnlockLogonSessions(SystemLogonSession); } else { //
// Update the flags to indicate that we do not have a password
//
KerbWriteLockLogonSessions(SystemLogonSession); SystemLogonSession->LogonSessionFlags |= (KERB_LOGON_LOCAL_ONLY | KERB_LOGON_NO_PASSWORD); KerbUnlockLogonSessions(SystemLogonSession); }
Status = STATUS_SUCCESS; Cleanup: if (SystemLogonSession != NULL) { KerbDereferenceLogonSession(SystemLogonSession); }
*ProtocolStatus = Status;
return(STATUS_SUCCESS); }
//+-------------------------------------------------------------------------
//
// Function: KerbNameLength
//
// Synopsis: returns length in bytes of variable portion of KERB_INTERNAL_NAME
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes: This code is (effectively) duplicated in
// KerbWOWNameLength. Make sure any changes
// made here are applied there as well.
//
//--------------------------------------------------------------------------
ULONG KerbNameLength( IN PKERB_INTERNAL_NAME Name ) { ULONG Length = 0; ULONG Index;
if (!ARGUMENT_PRESENT(Name)) { return(0); } Length = sizeof(KERB_INTERNAL_NAME) - sizeof(UNICODE_STRING) + Name->NameCount * sizeof(UNICODE_STRING) ; for (Index = 0; Index < Name->NameCount ;Index++ ) { Length += Name->Names[Index].Length; } Length = ROUND_UP_COUNT(Length, sizeof(LPWSTR)); return(Length); }
ULONG KerbStringNameLength( IN PKERB_INTERNAL_NAME Name ) { ULONG Length = 0; ULONG Index;
Length = Name->NameCount * sizeof(WCHAR); // for separators & null terminator
for (Index = 0; Index < Name->NameCount ;Index++ ) { Length += Name->Names[Index].Length; } return(Length); }
//+-------------------------------------------------------------------------
//
// Function: KerbPutKdcName
//
// Synopsis: Copies a Kdc name to a buffer
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes: This code is (effectively) duplicated in
// KerbPutWOWKdcName. Make sure any changes
// made here are applied there as well.
//
//--------------------------------------------------------------------------
VOID KerbPutKdcName( IN PKERB_INTERNAL_NAME InputName, OUT PKERB_EXTERNAL_NAME * OutputName, IN LONG_PTR Offset, IN OUT PBYTE * Where ) { ULONG Index; PKERB_INTERNAL_NAME LocalName = (PKERB_INTERNAL_NAME) *Where;
if (!ARGUMENT_PRESENT(InputName)) { *OutputName = NULL; return; } *Where += sizeof(KERB_INTERNAL_NAME) - sizeof(UNICODE_STRING) + InputName->NameCount * sizeof(UNICODE_STRING); LocalName->NameType = InputName->NameType; LocalName->NameCount = InputName->NameCount;
for (Index = 0; Index < InputName->NameCount ; Index++ ) { LocalName->Names[Index].Length = LocalName->Names[Index].MaximumLength = InputName->Names[Index].Length; LocalName->Names[Index].Buffer = (LPWSTR) (*Where + Offset); RtlCopyMemory( *Where, InputName->Names[Index].Buffer, InputName->Names[Index].Length ); *Where += InputName->Names[Index].Length; } *Where = (PBYTE) ROUND_UP_POINTER(*Where,sizeof(LPWSTR)); *OutputName = (PKERB_EXTERNAL_NAME) ((PBYTE) LocalName + Offset); }
//+-------------------------------------------------------------------------
//
// Function: KerbPutKdcNameAsString
//
// Synopsis: Copies a KERB_INTERNAL_NAME into a buffer
//
// Effects:
//
// Arguments: InputString - String to 'put'
// OutputString - Receives 'put' string
// Offset - Difference in addresses of local and client buffers.
// Where - Location in local buffer to place string.
//
// Requires:
//
// Returns:
//
// Notes: This code is (effectively) duplicated in
// KerbPutKdcNameAsWOWString. Make sure any
// changes made here are applied there as well.
//
//--------------------------------------------------------------------------
VOID KerbPutKdcNameAsString( IN PKERB_INTERNAL_NAME InputName, OUT PUNICODE_STRING OutputName, IN LONG_PTR Offset, IN OUT PBYTE * Where ) { USHORT Index;
OutputName->Buffer = (LPWSTR) (*Where + Offset); OutputName->Length = 0; OutputName->MaximumLength = 0;
for (Index = 0; Index < InputName->NameCount ; Index++ ) { RtlCopyMemory( *Where, InputName->Names[Index].Buffer, InputName->Names[Index].Length ); *Where += InputName->Names[Index].Length; OutputName->Length = OutputName->Length + InputName->Names[Index].Length; if (Index == (InputName->NameCount - 1)) { *((LPWSTR) *Where) = L'\0'; OutputName->MaximumLength = OutputName->Length + sizeof(WCHAR); } else { *((LPWSTR) *Where) = L'/'; OutputName->Length += sizeof(WCHAR); } *Where += sizeof(WCHAR); } }
//+-------------------------------------------------------------------------
//
// Function: KerbPutString
//
// Synopsis: Copies a UNICODE_STRING into a buffer
//
// Effects:
//
// Arguments: InputString - String to 'put'
// OutputString - Receives 'put' string
// Offset - Difference in addresses of local and client buffers.
// Where - Location in local buffer to place string.
//
// Requires:
//
// Returns:
//
// Notes: This code is (effectively) duplicated in
// KerbPutWOWString. Make sure any changes
// made here are applied there as well.
//
//--------------------------------------------------------------------------
VOID KerbPutString( IN PUNICODE_STRING InputString, OUT PUNICODE_STRING OutputString, IN LONG_PTR Offset, IN OUT PBYTE * Where ) { OutputString->Length = OutputString->MaximumLength = InputString->Length; OutputString->Buffer = (LPWSTR) (*Where + Offset); RtlCopyMemory( *Where, InputString->Buffer, InputString->Length ); *Where += InputString->Length; }
//+-------------------------------------------------------------------------
//
// Function: ComputeTicketCacheSize
//
// Synopsis: Computes the size necessary to store contents of a ticket cache
//
// Effects:
//
// Arguments: TicketCache cache to compute the size of
// WowClient is this a WOW client? (64-bit only)
// CacheSize used to append the size of cache
// CacheEntries used to append the number of entries
//
// Requires:
//
// Returns: Nothing
//
// Notes:
//
//
//--------------------------------------------------------------------------
void KerbComputeTicketCacheSize( IN KERB_PRIMARY_CREDENTIAL * PrimaryCredentials, IN BOOLEAN WowClient, IN OUT ULONG * CacheSize, IN OUT ULONG * CacheEntries ) { DsysAssert( CacheSize ); DsysAssert( CacheEntries );
#if _WIN64
ULONG CacheEntrySize = WowClient ? (ULONG) sizeof( KERB_TICKET_CACHE_INFO_WOW64 ) : (ULONG) sizeof( KERB_TICKET_CACHE_INFO ); #else
ULONG CacheEntrySize = sizeof( KERB_TICKET_CACHE_INFO ); DsysAssert( WowClient == FALSE ); #endif // _WIN64
KERB_TICKET_CACHE * TicketCaches[3] = { &PrimaryCredentials->AuthenticationTicketCache, &PrimaryCredentials->ServerTicketCache, &PrimaryCredentials->S4UTicketCache };
if ( *CacheSize == 0 ) {
*CacheSize = FIELD_OFFSET( KERB_QUERY_TKT_CACHE_RESPONSE, Tickets ); }
for ( ULONG i = 0 ; i < 3 ; i++ ) {
KERB_TICKET_CACHE * TicketCache = TicketCaches[i];
for ( PLIST_ENTRY ListEntry = TicketCache->CacheEntries.Flink ; ListEntry != &TicketCache->CacheEntries ; ListEntry = ListEntry->Flink ) {
KERB_TICKET_CACHE_ENTRY * CacheEntry;
CacheEntry= CONTAINING_RECORD( ListEntry, KERB_TICKET_CACHE_ENTRY, ListEntry.Next );
DsysAssert( CacheEntry->ServiceName != NULL );
*CacheEntries += 1;
*CacheSize += CacheEntrySize + KerbStringNameLength( CacheEntry->ServiceName ) + CacheEntry->DomainName.Length; } } }
void KerbBuildQueryTicketCacheResponse( IN KERB_PRIMARY_CREDENTIAL * PrimaryCredentials, IN PKERB_QUERY_TKT_CACHE_RESPONSE CacheResponse, IN BOOLEAN WowClient, IN OUT LONG_PTR * Offset, IN OUT PBYTE * Where, IN OUT ULONG * Index ) { DsysAssert( Offset ); DsysAssert( Where ); DsysAssert( Index );
#if _WIN64
PKERB_QUERY_TKT_CACHE_RESPONSE_WOW64 CacheResponseWOW64 = (PKERB_QUERY_TKT_CACHE_RESPONSE_WOW64) CacheResponse; ULONG CacheEntrySize = WowClient ? (ULONG) sizeof( KERB_TICKET_CACHE_INFO_WOW64 ) : (ULONG) sizeof( KERB_TICKET_CACHE_INFO ); #else
ULONG CacheEntrySize = sizeof( KERB_TICKET_CACHE_INFO ); DsysAssert( WowClient == FALSE ); #endif // _WIN64
KERB_TICKET_CACHE * TicketCaches[3] = { &PrimaryCredentials->AuthenticationTicketCache, &PrimaryCredentials->ServerTicketCache, &PrimaryCredentials->S4UTicketCache };
if ( *Where == NULL ) {
*Where = ( PBYTE )( CacheResponse->Tickets ) + CacheResponse->CountOfTickets * CacheEntrySize; }
for ( ULONG i = 0 ; i < 3 ; i++ ) {
KERB_TICKET_CACHE * TicketCache = TicketCaches[i];
for ( PLIST_ENTRY ListEntry = TicketCache->CacheEntries.Flink ; ListEntry != &TicketCache->CacheEntries ; ListEntry = ListEntry->Flink ) {
KERB_TICKET_CACHE_ENTRY * CacheEntry;
CacheEntry= CONTAINING_RECORD( ListEntry, KERB_TICKET_CACHE_ENTRY, ListEntry.Next );
#if _WIN64
if ( !WowClient ) { #endif // _WIN64
CacheResponse->Tickets[*Index].StartTime = CacheEntry->StartTime; CacheResponse->Tickets[*Index].EndTime = CacheEntry->EndTime; CacheResponse->Tickets[*Index].RenewTime = CacheEntry->RenewUntil; CacheResponse->Tickets[*Index].EncryptionType = (LONG) CacheEntry->Ticket.encrypted_part.encryption_type; CacheResponse->Tickets[*Index].TicketFlags = CacheEntry->TicketFlags; CacheResponse->Tickets[*Index].ServerName.Buffer = (LPWSTR) (*Where + *Offset); CacheResponse->Tickets[*Index].ServerName.Length = CacheEntry->ServiceName->Names[0].Length; CacheResponse->Tickets[*Index].ServerName.MaximumLength = CacheEntry->ServiceName->Names[0].Length;
KerbPutString( &CacheEntry->DomainName, &CacheResponse->Tickets[*Index].RealmName, *Offset, Where );
KerbPutKdcNameAsString( CacheEntry->ServiceName, &CacheResponse->Tickets[*Index].ServerName, *Offset, Where );
#if _WIN64
} else {
CacheResponseWOW64->Tickets[*Index].StartTime = CacheEntry->StartTime; CacheResponseWOW64->Tickets[*Index].EndTime = CacheEntry->EndTime; CacheResponseWOW64->Tickets[*Index].RenewTime = CacheEntry->RenewUntil; CacheResponseWOW64->Tickets[*Index].EncryptionType = ( LONG )CacheEntry->Ticket.encrypted_part.encryption_type; CacheResponseWOW64->Tickets[*Index].TicketFlags = CacheEntry->TicketFlags; CacheResponseWOW64->Tickets[*Index].ServerName.Buffer = PtrToUlong (*Where + *Offset); CacheResponseWOW64->Tickets[*Index].ServerName.Length = CacheEntry->ServiceName->Names[0].Length; CacheResponseWOW64->Tickets[*Index].ServerName.MaximumLength = CacheEntry->ServiceName->Names[0].Length;
KerbPutWOWString( &CacheEntry->DomainName, &CacheResponseWOW64->Tickets[*Index].RealmName, *Offset, Where );
KerbPutKdcNameAsWOWString( CacheEntry->ServiceName, &CacheResponseWOW64->Tickets[*Index].ServerName, *Offset, Where ); }
#endif // _WIN64
(*Index)++; } } }
//+-------------------------------------------------------------------------
//
// Function: KerbQueryTicketCache
//
// Synopsis: Retrieves the list of tickets for the specified logon session
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbQueryTicketCache( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; PKERB_QUERY_TKT_CACHE_REQUEST CacheRequest = ( PKERB_QUERY_TKT_CACHE_REQUEST )ProtocolSubmitBuffer; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonId; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_QUERY_TKT_CACHE_RESPONSE CacheResponse = NULL; PKERB_QUERY_TKT_CACHE_RESPONSE ClientCacheResponse = NULL; ULONG CacheSize = 0; ULONG CacheEntries = 0; BOOLEAN LockHeld = FALSE; LONG_PTR Offset; PBYTE Where = NULL; ULONG Index = 0;
//
// Verify the request.
//
if ( SubmitBufferLength < sizeof( KERB_QUERY_TKT_CACHE_REQUEST )) {
Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
//
// Find the caller's logon id & TCB status
//
Status = LsaFunctions->GetClientInfo( &ClientInfo );
if ( !NT_SUCCESS( Status )) {
goto Cleanup; }
//
// If the caller did not provide a logon id, use the caller's logon id.
//
if ( RtlIsZeroLuid( &CacheRequest->LogonId )) {
LogonId = &ClientInfo.LogonId;
} else if ( !ClientInfo.HasTcbPrivilege ) {
//
// Caller must have TCB privilege in order to access to someone
// else's ticket cache.
//
Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup;
} else {
LogonId = &CacheRequest->LogonId; }
LogonSession = KerbReferenceLogonSession( LogonId, FALSE // don't unlink
);
if ( LogonSession == NULL ) {
Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
#if _WIN64
SECPKG_CALL_INFO CallInfo;
if( !LsaFunctions->GetCallInfo( &CallInfo )) {
Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
#endif // _WIN64
//
// Prowl through the caches and find all the tickets
//
DsysAssert( !LockHeld ); KerbReadLockLogonSessions(LogonSession); KerbReadLockTicketCache(); LockHeld = TRUE;
//
// Calculate the size needed for all the ticket information
//
KerbComputeTicketCacheSize( &LogonSession->PrimaryCredentials, #if _WIN64
(( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) != 0 ), #else
FALSE, #endif
&CacheSize, &CacheEntries );
//
// Now allocate two copies of the structure - one in our process, one in
// the client's process. We then build the structure in our process but
// with pointer valid in the client's process.
//
SafeAllocaAllocate(CacheResponse, CacheSize);
if ( CacheResponse == NULL ) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
Status = LsaFunctions->AllocateClientBuffer( NULL, CacheSize, ( PVOID * )&ClientCacheResponse );
if ( !NT_SUCCESS( Status )) {
goto Cleanup; }
Offset = ( LONG_PTR )(( PBYTE )ClientCacheResponse - ( PBYTE )CacheResponse );
//
// Build up the return structure
//
CacheResponse->MessageType = KerbQueryTicketCacheMessage; CacheResponse->CountOfTickets = CacheEntries;
KerbBuildQueryTicketCacheResponse( &LogonSession->PrimaryCredentials, CacheResponse, #if _WIN64
(( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) != 0 ), #else
FALSE, #endif
&Offset, &Where, &Index );
//
// Copy the structure to the client's address space
//
Status = LsaFunctions->CopyToClientBuffer( NULL, CacheSize, ClientCacheResponse, CacheResponse );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
*ProtocolReturnBuffer = ClientCacheResponse; ClientCacheResponse = NULL; *ReturnBufferLength = CacheSize;
Cleanup:
if (LockHeld) { KerbUnlockTicketCache(); KerbUnlockLogonSessions( LogonSession ); }
if (LogonSession != NULL) { KerbDereferenceLogonSession( LogonSession ); }
SafeAllocaFree(CacheResponse);
if (ClientCacheResponse != NULL) { LsaFunctions->FreeClientBuffer( NULL, ClientCacheResponse ); }
*ProtocolStatus = Status; return STATUS_SUCCESS; }
//+-------------------------------------------------------------------------
//
// Function: ComputeTicketCacheSizeEx
//
// Synopsis: Computes the size necessary to store contents of a ticket cache
//
// Effects:
//
// Arguments: TicketCache cache to compute the size of
// WowClient is this a WOW client (64-bit only)
// CacheSize used to append the size of cache
// CacheEntries used to append the number of entries
//
// Requires:
//
// Returns: Nothing
//
// Notes:
//
//
//--------------------------------------------------------------------------
void KerbComputeTicketCacheSizeEx( IN KERB_PRIMARY_CREDENTIAL * PrimaryCredentials, IN BOOLEAN WowClient, IN OUT ULONG * CacheSize, IN OUT ULONG * CacheEntries ) { DsysAssert( CacheSize ); DsysAssert( CacheEntries );
#if _WIN64
ULONG CacheEntrySize = WowClient ? (ULONG) sizeof( KERB_TICKET_CACHE_INFO_EX_WOW64 ) : (ULONG) sizeof( KERB_TICKET_CACHE_INFO_EX ); #else
ULONG CacheEntrySize = sizeof( KERB_TICKET_CACHE_INFO_EX ); DsysAssert( WowClient == FALSE ); #endif // _WIN64
KERB_TICKET_CACHE * TicketCaches[3] = { &PrimaryCredentials->AuthenticationTicketCache, &PrimaryCredentials->ServerTicketCache, &PrimaryCredentials->S4UTicketCache };
if ( *CacheSize == 0 ) {
*CacheSize = FIELD_OFFSET( KERB_QUERY_TKT_CACHE_EX_RESPONSE, Tickets ); }
for ( ULONG i = 0 ; i < 3 ; i++ ) {
KERB_TICKET_CACHE * TicketCache = TicketCaches[i];
for ( PLIST_ENTRY ListEntry = TicketCache->CacheEntries.Flink ; ListEntry != &TicketCache->CacheEntries ; ListEntry = ListEntry->Flink ) {
KERB_TICKET_CACHE_ENTRY * CacheEntry;
CacheEntry= CONTAINING_RECORD( ListEntry, KERB_TICKET_CACHE_ENTRY, ListEntry.Next );
DsysAssert( CacheEntry->ServiceName != NULL );
*CacheEntries += 1;
*CacheSize += CacheEntrySize + // client name
PrimaryCredentials->UserName.Length + // client realm
PrimaryCredentials->DomainName.Length + // server name
KerbStringNameLength( CacheEntry->ServiceName ) + // server realm
CacheEntry->DomainName.Length; } } }
void KerbBuildQueryTicketCacheResponseEx( IN KERB_PRIMARY_CREDENTIAL * PrimaryCredentials, IN PKERB_QUERY_TKT_CACHE_EX_RESPONSE CacheResponse, IN BOOLEAN WowClient, IN OUT LONG_PTR * Offset, IN OUT PBYTE * Where, IN OUT ULONG * Index ) { DsysAssert( Offset ); DsysAssert( Where ); DsysAssert( Index );
#if _WIN64
PKERB_QUERY_TKT_CACHE_EX_RESPONSE_WOW64 CacheResponseWOW64 = (PKERB_QUERY_TKT_CACHE_EX_RESPONSE_WOW64) CacheResponse; ULONG CacheEntrySize = WowClient ? (ULONG) sizeof( KERB_TICKET_CACHE_INFO_EX_WOW64 ) : (ULONG) sizeof( KERB_TICKET_CACHE_INFO_EX ); #else
ULONG CacheEntrySize = sizeof( KERB_TICKET_CACHE_INFO_EX ); DsysAssert( WowClient == FALSE ); #endif // _WIN64
KERB_TICKET_CACHE * TicketCaches[3] = { &PrimaryCredentials->AuthenticationTicketCache, &PrimaryCredentials->ServerTicketCache, &PrimaryCredentials->S4UTicketCache };
if ( *Where == NULL ) {
*Where = ( PBYTE )( CacheResponse->Tickets ) + CacheResponse->CountOfTickets * CacheEntrySize; }
for ( ULONG i = 0 ; i < 3 ; i++ ) {
KERB_TICKET_CACHE * TicketCache = TicketCaches[i];
for ( PLIST_ENTRY ListEntry = TicketCache->CacheEntries.Flink ; ListEntry != &TicketCache->CacheEntries ; ListEntry = ListEntry->Flink ) {
KERB_TICKET_CACHE_ENTRY * CacheEntry;
CacheEntry= CONTAINING_RECORD( ListEntry, KERB_TICKET_CACHE_ENTRY, ListEntry.Next );
#if _WIN64
if ( !WowClient ) { #endif // _WIN64
CacheResponse->Tickets[*Index].StartTime = CacheEntry->StartTime; CacheResponse->Tickets[*Index].EndTime = CacheEntry->EndTime; CacheResponse->Tickets[*Index].RenewTime = CacheEntry->RenewUntil; CacheResponse->Tickets[*Index].EncryptionType = ( LONG )CacheEntry->Ticket.encrypted_part.encryption_type; CacheResponse->Tickets[*Index].TicketFlags = CacheEntry->TicketFlags;
KerbPutString( &PrimaryCredentials->UserName, &CacheResponse->Tickets[*Index].ClientName, *Offset, Where );
KerbPutString( &PrimaryCredentials->DomainName, &CacheResponse->Tickets[*Index].ClientRealm, *Offset, Where );
KerbPutKdcNameAsString( CacheEntry->ServiceName, &CacheResponse->Tickets[*Index].ServerName, *Offset, Where );
KerbPutString( &CacheEntry->DomainName, &CacheResponse->Tickets[*Index].ServerRealm, *Offset, Where );
#if _WIN64
} else {
CacheResponseWOW64->Tickets[*Index].StartTime = CacheEntry->StartTime; CacheResponseWOW64->Tickets[*Index].EndTime = CacheEntry->EndTime; CacheResponseWOW64->Tickets[*Index].RenewTime = CacheEntry->RenewUntil; CacheResponseWOW64->Tickets[*Index].EncryptionType = ( LONG )CacheEntry->Ticket.encrypted_part.encryption_type; CacheResponseWOW64->Tickets[*Index].TicketFlags = CacheEntry->TicketFlags;
KerbPutWOWString( &PrimaryCredentials->UserName, &CacheResponseWOW64->Tickets[*Index].ClientName, *Offset, Where );
KerbPutWOWString( &PrimaryCredentials->DomainName, &CacheResponseWOW64->Tickets[*Index].ClientRealm, *Offset, Where );
KerbPutKdcNameAsWOWString( CacheEntry->ServiceName, &CacheResponseWOW64->Tickets[*Index].ServerName, *Offset, Where );
KerbPutWOWString( &CacheEntry->DomainName, &CacheResponseWOW64->Tickets[*Index].ServerRealm, *Offset, Where ); }
#endif // _WIN64
(*Index)++; } } }
//+-------------------------------------------------------------------------
//
// Function: KerbAddExtraCredential
//
// Synopsis: Retrieves the list of tickets for the specified logon session
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbAddExtraCredential( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) {
NTSTATUS Status; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonId; PKERB_ADD_CREDENTIALS_REQUEST AddCredRequest = (PKERB_ADD_CREDENTIALS_REQUEST) ProtocolSubmitBuffer; PKERB_LOGON_SESSION LogonSession = NULL;
if (ARGUMENT_PRESENT( ReturnBufferLength )) { *ReturnBufferLength = 0; }
if (ARGUMENT_PRESENT( ProtocolReturnBuffer )) { *ProtocolReturnBuffer = NULL; }
if ( SubmitBufferSize < sizeof( KERB_ADD_CREDENTIALS_REQUEST )) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
#if _WIN64
SECPKG_CALL_INFO CallInfo;
if( !LsaFunctions->GetCallInfo( &CallInfo )) { Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
if (( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) != 0) { Status = STATUS_NOT_SUPPORTED; goto Cleanup; }
#endif // _WIN64
Status = LsaFunctions->GetClientInfo( &ClientInfo );
if ( !NT_SUCCESS( Status )) { goto Cleanup; }
//
// If the caller did not provide a logon id, use the caller's logon id.
//
if ( RtlIsZeroLuid( &AddCredRequest->LogonId )) { LogonId = &ClientInfo.LogonId; } else if ( !ClientInfo.HasTcbPrivilege ) { Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup; } else { LogonId = &AddCredRequest->LogonId; }
LogonSession = KerbReferenceLogonSession( LogonId, FALSE );
if ( LogonSession == NULL ) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
NULL_RELOCATE_ONE( &AddCredRequest->DomainName ); NULL_RELOCATE_ONE( &AddCredRequest->Password ); NULL_RELOCATE_ONE( &AddCredRequest->UserName );
//
// We will default nothing for this request.
//
if (( AddCredRequest->DomainName.Length == 0 ) || ( AddCredRequest->UserName.Length == 0 )) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
if (((AddCredRequest->Flags & (KERB_REQUEST_ADD_CREDENTIAL | KERB_REQUEST_REPLACE_CREDENTIAL )) != 0) && ( AddCredRequest->Password.Length == 0 ))
{ Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
Status = KerbAddExtraCredentialsToLogonSession( LogonSession, AddCredRequest );
if (!NT_SUCCESS( Status )) { D_DebugLog((DEB_ERROR, "KerbAddExtraCredentialToLogonSession failed %x\n", Status)); goto Cleanup; }
Cleanup:
if ( LogonSession != NULL ) { KerbDereferenceLogonSession( LogonSession ); } *ProtocolStatus = Status; return STATUS_SUCCESS; }
//+-------------------------------------------------------------------------
//
// Function: KerbQueryTicketCacheEx
//
// Synopsis: Retrieves the list of tickets for the specified logon session
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbQueryTicketCacheEx( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; PKERB_QUERY_TKT_CACHE_REQUEST CacheRequest = ( PKERB_QUERY_TKT_CACHE_REQUEST )ProtocolSubmitBuffer; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonId; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_QUERY_TKT_CACHE_EX_RESPONSE CacheResponse = NULL; PKERB_QUERY_TKT_CACHE_EX_RESPONSE ClientCacheResponse = NULL; ULONG CacheSize = 0; ULONG CacheEntries = 0; BOOLEAN TicketCacheLocked = FALSE; BOOLEAN CredmanLocked = FALSE; PLIST_ENTRY ListEntry; LONG_PTR Offset; PBYTE Where = NULL; ULONG Index = 0;
//
// Verify the request.
//
if ( SubmitBufferLength < sizeof( KERB_QUERY_TKT_CACHE_REQUEST )) {
Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
//
// Find the caller's logon id & TCB status
//
Status = LsaFunctions->GetClientInfo( &ClientInfo );
if ( !NT_SUCCESS( Status )) {
goto Cleanup; }
//
// If the caller did not provide a logon id, use the caller's logon id.
//
if ( RtlIsZeroLuid( &CacheRequest->LogonId )) {
LogonId = &ClientInfo.LogonId;
} else if ( !ClientInfo.HasTcbPrivilege ) {
//
// Caller must have TCB privilege in order to access to someone
// else's ticket cache.
//
Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup;
} else {
LogonId = &CacheRequest->LogonId; }
LogonSession = KerbReferenceLogonSession( LogonId, FALSE );
if ( LogonSession == NULL ) {
Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
#if _WIN64
SECPKG_CALL_INFO CallInfo;
if( !LsaFunctions->GetCallInfo( &CallInfo )) {
Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
#endif // _WIN64
//
// Prowl through the caches and find all the tickets
//
DsysAssert( !TicketCacheLocked ); KerbReadLockLogonSessions( LogonSession );
KerbLockList( &LogonSession->CredmanCredentials ); CredmanLocked = TRUE;
KerbReadLockTicketCache(); TicketCacheLocked = TRUE;
//
// Calculate the size needed for all the ticket information
//
KerbComputeTicketCacheSizeEx( &LogonSession->PrimaryCredentials, #if _WIN64
(( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) != 0 ), #else
FALSE, #endif
&CacheSize, &CacheEntries );
for ( ListEntry = LogonSession->CredmanCredentials.List.Flink; ListEntry != &LogonSession->CredmanCredentials.List; ListEntry = ListEntry->Flink ) {
PKERB_CREDMAN_CRED CredmanCred = CONTAINING_RECORD( ListEntry, KERB_CREDMAN_CRED, ListEntry.Next );
if ( CredmanCred->SuppliedCredentials == NULL ) {
continue; }
KerbComputeTicketCacheSizeEx( CredmanCred->SuppliedCredentials, #if _WIN64
(( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) != 0 ), #else
FALSE, #endif
&CacheSize, &CacheEntries ); }
//
// Now allocate two copies of the structure - one in our process, one in
// the client's process. We then build the structure in our process but
// with pointer valid in the client's process.
//
SafeAllocaAllocate(CacheResponse, CacheSize);
if ( CacheResponse == NULL ) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
Status = LsaFunctions->AllocateClientBuffer( NULL, CacheSize, ( PVOID * )&ClientCacheResponse );
if ( !NT_SUCCESS( Status )) {
goto Cleanup; }
Offset = ( LONG_PTR )(( PBYTE )ClientCacheResponse - ( PBYTE )CacheResponse );
//
// Build up the return structure
//
CacheResponse->MessageType = KerbQueryTicketCacheExMessage; CacheResponse->CountOfTickets = CacheEntries;
KerbBuildQueryTicketCacheResponseEx( &LogonSession->PrimaryCredentials, CacheResponse, #if _WIN64
(( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) != 0 ), #else
FALSE, #endif
&Offset, &Where, &Index );
for ( ListEntry = LogonSession->CredmanCredentials.List.Flink; ListEntry != &LogonSession->CredmanCredentials.List; ListEntry = ListEntry->Flink ) {
PKERB_CREDMAN_CRED CredmanCred = CONTAINING_RECORD( ListEntry, KERB_CREDMAN_CRED, ListEntry.Next );
if ( CredmanCred->SuppliedCredentials == NULL ) {
continue; }
KerbBuildQueryTicketCacheResponseEx( CredmanCred->SuppliedCredentials, CacheResponse, #if _WIN64
(( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) != 0 ), #else
FALSE, #endif
&Offset, &Where, &Index ); }
//
// Copy the structure to the client's address space
//
Status = LsaFunctions->CopyToClientBuffer( NULL, CacheSize, ClientCacheResponse, CacheResponse );
if ( !NT_SUCCESS( Status )) {
goto Cleanup; }
*ProtocolReturnBuffer = ClientCacheResponse; ClientCacheResponse = NULL; *ReturnBufferLength = CacheSize;
Cleanup:
if ( CredmanLocked ) {
KerbUnlockList( &LogonSession->CredmanCredentials ); }
if ( TicketCacheLocked ) {
KerbUnlockTicketCache(); KerbUnlockLogonSessions( LogonSession ); }
if ( LogonSession != NULL ) {
KerbDereferenceLogonSession( LogonSession ); }
SafeAllocaFree( CacheResponse );
if ( ClientCacheResponse != NULL ) {
LsaFunctions->FreeClientBuffer( NULL, ClientCacheResponse ); }
*ProtocolStatus = Status; return STATUS_SUCCESS; }
//+-------------------------------------------------------------------------
//
// Function: KerbPackExternalTicket
//
// Synopsis: Marshalls a ticket cache entry for return to the caller
//
// Effects: Allocates memory in client's address space
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KerbPackExternalTicket( IN PKERB_TICKET_CACHE_ENTRY CacheEntry, IN BOOL RetrieveTicketAsKerbCred, IN BOOL AllowTgtSessionKey, OUT PULONG ClientTicketSize, OUT PUCHAR * ClientTicket ) { ULONG TicketSize = 0; NTSTATUS Status = STATUS_SUCCESS; PKERB_EXTERNAL_TICKET TicketResponse = NULL; PBYTE ClientTicketResponse = NULL; KERB_MESSAGE_BUFFER EncodedTicket = {0}; LONG_PTR Offset; PBYTE Where; BOOL fTicketOnStack = FALSE; KERB_TICKET_CACHE_ENTRY CacheEntryT;
*ClientTicket = NULL; *ClientTicketSize = 0;
#if _WIN64
SECPKG_CALL_INFO CallInfo;
//
// Return a 32-bit external ticket if this is a WOW caller
//
if(!LsaFunctions->GetCallInfo(&CallInfo)) { Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
#endif // _WIN64
//
// 448010: do not fess up the session key for primary TGTs unless
// the caller is trusted or policy is set appropriately
//
if ( !AllowTgtSessionKey && ( CacheEntry->CacheFlags & KERB_TICKET_CACHE_PRIMARY_TGT )) {
CacheEntryT = *CacheEntry; RtlZeroMemory( &CacheEntryT.SessionKey, sizeof( KERB_ENCRYPTION_KEY )); CacheEntry = &CacheEntryT; }
//
// Encode the ticket
//
if ( RetrieveTicketAsKerbCred ) { Status = KerbBuildKerbCred( NULL, // service ticket
CacheEntry, &EncodedTicket.Buffer, &EncodedTicket.BufferSize );
if ( !NT_SUCCESS( Status )) { goto Cleanup; } } else { KERBERR KerbErr;
KerbErr = KerbPackData( &CacheEntry->Ticket, KERB_TICKET_PDU, &EncodedTicket.BufferSize, &EncodedTicket.Buffer );
if (!KERB_SUCCESS(KerbErr)) { Status = KerbMapKerbError(KerbErr); goto Cleanup; } }
//
// NOTE: The 64-bit code below is (effectively) duplicated in
// the WOW helper routine. If modifying one, make sure
// to apply the change(s) to the other as well.
//
#if _WIN64
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { Status = KerbPackExternalWOWTicket(CacheEntry, &EncodedTicket, &TicketResponse, &ClientTicketResponse, &TicketSize);
if (!NT_SUCCESS(Status)) { goto Cleanup; } } else {
#endif // _WIN64
TicketSize = sizeof(KERB_EXTERNAL_TICKET) + CacheEntry->DomainName.Length + CacheEntry->TargetDomainName.Length + CacheEntry->ClientDomainName.Length + CacheEntry->SessionKey.keyvalue.length + KerbNameLength(CacheEntry->ServiceName) + KerbNameLength(CacheEntry->TargetName) + KerbNameLength(CacheEntry->ClientName) + EncodedTicket.BufferSize ;
//
// Now allocate two copies of the structure - one in our process,
// one in the client's process. We then build the structure in our
// process but with pointer valid in the client's process
//
SafeAllocaAllocate(TicketResponse, TicketSize); fTicketOnStack = TRUE;
if (TicketResponse == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
Status = LsaFunctions->AllocateClientBuffer( NULL, TicketSize, (PVOID *) &ClientTicketResponse ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
Offset = (LONG_PTR) (ClientTicketResponse - (PBYTE) TicketResponse);
Where = ((PUCHAR) (TicketResponse + 1));
//
// Copy the non-pointer fields
//
TicketResponse->TicketFlags = CacheEntry->TicketFlags; TicketResponse->Flags = 0; TicketResponse->KeyExpirationTime.QuadPart = 0; TicketResponse->StartTime = CacheEntry->StartTime; TicketResponse->EndTime = CacheEntry->EndTime; TicketResponse->RenewUntil = CacheEntry->RenewUntil; TicketResponse->TimeSkew = CacheEntry->TimeSkew; TicketResponse->SessionKey.KeyType = CacheEntry->SessionKey.keytype;
//
// Copy the structure to the client's address space
//
//
// These are PVOID aligned
//
//
// Make sure the two name types are the same
//
DsysAssert(sizeof(KERB_INTERNAL_NAME) == sizeof(KERB_EXTERNAL_NAME)); DsysAssert(FIELD_OFFSET(KERB_INTERNAL_NAME,NameType) == FIELD_OFFSET(KERB_EXTERNAL_NAME,NameType)); DsysAssert(FIELD_OFFSET(KERB_INTERNAL_NAME,NameCount) == FIELD_OFFSET(KERB_EXTERNAL_NAME,NameCount)); DsysAssert(FIELD_OFFSET(KERB_INTERNAL_NAME,Names) == FIELD_OFFSET(KERB_EXTERNAL_NAME,Names));
KerbPutKdcName( CacheEntry->ServiceName, &TicketResponse->ServiceName, Offset, &Where );
KerbPutKdcName( CacheEntry->TargetName, &TicketResponse->TargetName, Offset, &Where );
KerbPutKdcName( CacheEntry->ClientName, &TicketResponse->ClientName, Offset, &Where );
//
// From here on, they are WCHAR aligned
//
KerbPutString( &CacheEntry->DomainName, &TicketResponse->DomainName, Offset, &Where );
KerbPutString( &CacheEntry->TargetDomainName, &TicketResponse->TargetDomainName, Offset, &Where );
KerbPutString( &CacheEntry->ClientDomainName, &TicketResponse->AltTargetDomainName, // ClientDomainName
Offset, &Where );
//
// And from here they are BYTE aligned
//
TicketResponse->SessionKey.Value = (PBYTE) (Where + Offset); RtlCopyMemory( Where, CacheEntry->SessionKey.keyvalue.value, CacheEntry->SessionKey.keyvalue.length ); Where += CacheEntry->SessionKey.keyvalue.length;
TicketResponse->SessionKey.Length = CacheEntry->SessionKey.keyvalue.length;
TicketResponse->EncodedTicketSize = EncodedTicket.BufferSize; TicketResponse->EncodedTicket = Where + Offset;
RtlCopyMemory( Where, EncodedTicket.Buffer, EncodedTicket.BufferSize );
Where += EncodedTicket.BufferSize;
DsysAssert(Where - ((PUCHAR) TicketResponse) == (LONG_PTR) TicketSize);
#if _WIN64
}
#endif // _WIN64
//
// Copy the mess to the client
//
Status = LsaFunctions->CopyToClientBuffer( NULL, TicketSize, ClientTicketResponse, TicketResponse ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
*ClientTicket = ClientTicketResponse; *ClientTicketSize = TicketSize;
ClientTicketResponse = NULL;
Cleanup:
if (EncodedTicket.Buffer != NULL) { MIDL_user_free(EncodedTicket.Buffer); }
if (ClientTicketResponse != NULL) { LsaFunctions->FreeClientBuffer( NULL, ClientTicketResponse ); }
if (fTicketOnStack) { SafeAllocaFree(TicketResponse); } else { KerbFree(TicketResponse); }
return(Status); }
KERBERR KerbBuildFullServiceName( IN OPTIONAL PUNICODE_STRING pDomainName, IN PUNICODE_STRING pServiceName, IN ULONG NameType, OUT PKERB_INTERNAL_NAME* ppFullServiceName ) { PKERB_INTERNAL_NAME pFinalName = NULL; PUCHAR pWhere; ULONG NameParts; ULONG NameLength = 0;
if ((NameType == KRB_NT_MS_PRINCIPAL) || (NameType == KRB_NT_MS_PRINCIPAL_AND_ID))
{ NameParts = 1; NameLength = (pDomainName ? pDomainName->Length : 0) + pServiceName->Length + 2 * sizeof(WCHAR); } else if ((NameType == KRB_NT_PRINCIPAL) || (NameType == KRB_NT_PRINCIPAL_AND_ID) || (NameType == KRB_NT_ENTERPRISE_PRINCIPAL) || (NameType == KRB_NT_ENT_PRINCIPAL_AND_ID)) { NameParts = 1; NameLength = pServiceName->Length + sizeof(WCHAR); } else { NameParts = pDomainName && pDomainName->Length ? 2 : 1; NameLength = (pDomainName ? pDomainName->Length : 0) + pServiceName->Length + 2 * sizeof(WCHAR); }
*ppFullServiceName = NULL;
pFinalName = (PKERB_INTERNAL_NAME) MIDL_user_allocate(KERB_INTERNAL_NAME_SIZE(NameParts) + NameLength); if (pFinalName == NULL) { return(KRB_ERR_GENERIC); } RtlZeroMemory( pFinalName, KERB_INTERNAL_NAME_SIZE(NameParts) + NameLength );
pWhere = (PUCHAR) pFinalName + KERB_INTERNAL_NAME_SIZE(NameParts); pFinalName->NameType = (USHORT) NameType; pFinalName->NameCount = (USHORT) NameParts;
if ((NameType == KRB_NT_MS_PRINCIPAL) || (NameType == KRB_NT_MS_PRINCIPAL_AND_ID)) { //
// If the domain name does not have an initial '\', reserve space for one
//
pFinalName->Names[0].Buffer = (PWSTR) pWhere;
// This is dependent on our naming conventions.
//
// The full service name is the '\' domain name ':' service name.
//
pFinalName->Names[0].Length = (USHORT) ((pDomainName ? pDomainName->Length : 0) + pServiceName->Length + sizeof(WCHAR));
pFinalName->Names[0].MaximumLength = pFinalName->Names[0].Length + sizeof(WCHAR);
if (pDomainName && pDomainName->Length) { RtlCopyMemory( pFinalName->Names[0].Buffer, pDomainName->Buffer, pDomainName->Length );
pWhere += pDomainName->Length; }
if (pDomainName && (pDomainName->Length != 0) && (pServiceName->Length != 0)) { *(PWSTR) pWhere = L'\\'; pWhere += sizeof(WCHAR); }
RtlCopyMemory( pWhere, pServiceName->Buffer, pServiceName->Length );
pWhere += pServiceName->Length; pFinalName->Names[0].Length = (USHORT)(pWhere - (PUCHAR) pFinalName->Names[0].Buffer); *(LPWSTR) pWhere = L'\0'; } else if ((NameType == KRB_NT_PRINCIPAL) || (NameType == KRB_NT_PRINCIPAL_AND_ID) || (NameType == KRB_NT_ENTERPRISE_PRINCIPAL)|| (NameType == KRB_NT_ENT_PRINCIPAL_AND_ID)) { //
// Principals have no domain name
//
pFinalName->Names[0].Length = pServiceName->Length; pFinalName->Names[0].MaximumLength = pServiceName->Length + sizeof(WCHAR); pFinalName->Names[0].Buffer = (PWSTR) pWhere;
RtlCopyMemory( pWhere, pServiceName->Buffer, pServiceName->Length ); pWhere += pServiceName->Length; *((LPWSTR) pWhere) = L'\0'; } else { pFinalName->Names[0].Length = pServiceName->Length; pFinalName->Names[0].MaximumLength = pServiceName->Length + sizeof(WCHAR); pFinalName->Names[0].Buffer = (PWSTR) pWhere;
RtlCopyMemory( pWhere, pServiceName->Buffer, pServiceName->Length ); pWhere += pServiceName->Length; *((PWSTR) pWhere) = L'\0'; pWhere += sizeof(WCHAR);
if (pDomainName && pDomainName->Length) { pFinalName->Names[1].Length = pDomainName->Length; pFinalName->Names[1].MaximumLength = pDomainName->Length + sizeof(WCHAR); pFinalName->Names[1].Buffer = (PWSTR) pWhere;
RtlCopyMemory( pWhere, pDomainName->Buffer, pDomainName->Length ); pWhere += pDomainName->Length; *((PWSTR) pWhere) = L'\0'; pWhere += sizeof(WCHAR); } }
*ppFullServiceName = pFinalName;
return (KDC_ERR_NONE); }
//+-------------------------------------------------------------------------
//
// Function: KerbTicketAsRequest
//
// Synopsis: Retrieves ticket via As Request
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbTicketAsRequest( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; KERBERR KerbErr;
KERB_TICKET_AS_REQUEST* pAsRequest = NULL; PBYTE pClientTicketResponse = NULL; ULONG TicketSize = 0; PKERB_LOGON_SESSION pLogonSession = NULL; LUID LogonId = {0};
PKERB_TICKET_CACHE_ENTRY pTicketCacheEntry = NULL; KERB_ENCRYPTION_KEY CredentialKey = {0};
PKERB_INTERNAL_NAME KdcServiceName = NULL; PKERB_INTERNAL_NAME ClientName = NULL; UNICODE_STRING ClientRealm = {0}; UNICODE_STRING CorrectRealm = {0}; ULONG RetryCount = KERB_CLIENT_REFERRAL_MAX; PKERB_MIT_REALM MitRealm = NULL; ULONG RequestFlags = 0; BOOLEAN UsingSuppliedCreds = FALSE; BOOLEAN UseWkstaRealm = TRUE; BOOLEAN MitRealmLogon = FALSE; BOOLEAN UsedPrimaryLogonCreds = FALSE;
pAsRequest = (KERB_TICKET_AS_REQUEST*) ProtocolSubmitBuffer;
//
// Verify the request
//
if ( !pAsRequest || (SubmitBufferSize < sizeof(KERB_TICKET_AS_REQUEST)) ) { D_DebugLog((DEB_ERROR, "KerbTicketAsRequest %p %d\n", pAsRequest, SubmitBufferSize)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
NULL_RELOCATE_ONE(&pAsRequest->ClientName); NULL_RELOCATE_ONE(&pAsRequest->ClientRealm); NULL_RELOCATE_ONE(&pAsRequest->ServerName); NULL_RELOCATE_ONE(&pAsRequest->ServerRealm); NULL_RELOCATE_ONE(&pAsRequest->ClientPassword);
D_DebugLog((DEB_WARN, "ClientRealm (%wZ), ClientName (%wZ), ClientPassword (%wZ), ServerRealm (%wZ), ServerName (%wZ)\n", &pAsRequest->ClientRealm, &pAsRequest->ClientName, &pAsRequest->ClientPassword, &pAsRequest->ServerRealm, &pAsRequest->ServerName));
Status = NtAllocateLocallyUniqueId( &LogonId );
if (NT_SUCCESS(Status)) { Status = KerbCreateLogonSession( &LogonId, &pAsRequest->ClientName, &pAsRequest->ClientRealm, &pAsRequest->ClientPassword, NULL, // no old password
PRIMARY_CRED_CLEAR_PASSWORD, KERB_LOGON_DUMMY_SESSION, &pLogonSession ); }
//
// Parse the name
//
if (NT_SUCCESS(Status)) { Status = KerbGetClientNameAndRealm( &pLogonSession->LogonId, &pLogonSession->PrimaryCredentials, NULL, &MitRealmLogon, UseWkstaRealm, &ClientName, &ClientRealm ); }
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// if we're doing a MIT logon, add the MIT logon flag
//
if (MitRealmLogon && UsedPrimaryLogonCreds) { pLogonSession->LogonSessionFlags |= KERB_LOGON_MIT_REALM; }
TicketAsRequestRestart:
D_DebugLog((DEB_TRACE, "KerbTicketAsRequest GetTicketRestart ClientRealm %wZ\n", &ClientRealm));
KerbErr = KerbBuildFullServiceName( &pAsRequest->ServerRealm, &pAsRequest->ServerName, pAsRequest->NameType, &KdcServiceName ); if (!KERB_SUCCESS(KerbErr)) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
D_DebugLog((DEB_TRACE, "KdcServiceName is ")); D_KerbPrintKdcName((DEB_TRACE, KdcServiceName));
Status = KerbGetAuthenticationTicket( pLogonSession, NULL, // no supplied cred
NULL, // no credman cred
KdcServiceName, &ClientRealm, ClientName, RequestFlags, KERB_TICKET_CACHE_PRIMARY_TGT, &pTicketCacheEntry, &CredentialKey, &CorrectRealm );
//
// If it failed but gave us another realm to try, go there
//
if (!NT_SUCCESS(Status) && (CorrectRealm.Length != 0)) { if (--RetryCount != 0) { KerbFreeKdcName(&KdcServiceName); KerbFreeString(&ClientRealm); ClientRealm = CorrectRealm; CorrectRealm.Buffer = NULL;
//
// Might be an MIT realm, in which case we'll need to adjust
// the client name. This will also populate the realm list
// with appropriate entries, so the KerbGetKdcBinding will not
// hit DNS again.
//
if (KerbLookupMitRealmWithSrvLookup( &ClientRealm, &MitRealm, FALSE, FALSE )) { D_DebugLog((DEB_TRACE,"Reacquiring client name & realm after referral\n")); UseWkstaRealm = FALSE; KerbFreeKdcName(&ClientName);
Status = KerbGetClientNameAndRealm( &LogonId, &pLogonSession->PrimaryCredentials, NULL, NULL, UseWkstaRealm, &ClientName, &ClientRealm );
if (!NT_SUCCESS(Status)) { goto Cleanup; } }
goto TicketAsRequestRestart; } else { // Tbd: Log error here? Max referrals reached..
goto Cleanup; } } else if ((Status == STATUS_NO_SUCH_USER) && UsingSuppliedCreds && UseWkstaRealm) { //
// We tried using the realm of the workstation and the account couldn't
// be found - try the realm from the UPN now.
//
if (KerbIsThisOurDomain(&ClientRealm)) { UseWkstaRealm = FALSE;
KerbFreeKdcName(&ClientName); KerbFreeString(&ClientRealm);
//
// Only do this if the caller did not supply a
// domain name
//
if (pLogonSession->PrimaryCredentials.DomainName.Length == 0) { Status = KerbGetClientNameAndRealm( &LogonId, &pLogonSession->PrimaryCredentials, NULL, NULL, UseWkstaRealm, &ClientName, &ClientRealm ); }
if (!NT_SUCCESS(Status)) { goto Cleanup; }
goto TicketAsRequestRestart; } }
if (pTicketCacheEntry == NULL) { Status = SEC_E_NO_CREDENTIALS; goto Cleanup; }
Status = KerbPackExternalTicket( pTicketCacheEntry, FALSE, TRUE, &TicketSize, &pClientTicketResponse );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
*ProtocolReturnBuffer = pClientTicketResponse; pClientTicketResponse = NULL; *ReturnBufferLength = TicketSize;
Cleanup:
if (pTicketCacheEntry) { KerbDereferenceTicketCacheEntry(pTicketCacheEntry); } if (pLogonSession) { KerbDereferenceLogonSession(pLogonSession); }
if (pClientTicketResponse) { LsaFunctions->FreeClientBuffer( NULL, pClientTicketResponse ); }
KerbFreeKey(&CredentialKey);
KerbFreeKdcName(&ClientName); KerbFreeString(&ClientRealm); KerbFreeString(&CorrectRealm);
KerbFreeKdcName(&KdcServiceName);
*ProtocolStatus = Status; return (STATUS_SUCCESS); }
NTSTATUS NTAPI KerbTicketAsRequestSafe( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status;
__try { Status = KerbTicketAsRequest( ClientRequest, ProtocolSubmitBuffer, ClientBufferBase, SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength, ProtocolStatus ); } __except (EXCEPTION_EXECUTE_HANDLER) { Status = GetExceptionCode(); D_DebugLog((DEB_ERROR, "KerbTicketAsRequest encountered an exception %#x\n", Status)); }
return Status; }
//+-------------------------------------------------------------------------
//
// Function: KerbRetrieveTicket
//
// Synopsis: Retrieves the initial ticket cache entry.
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbRetrieveTicket( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonId; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_QUERY_TKT_CACHE_REQUEST CacheRequest; PBYTE ClientTicketResponse = NULL; PKERB_TICKET_CACHE_ENTRY CacheEntry = NULL; ULONG TicketSize = 0;
//
// Verify the request.
//
if (SubmitBufferLength < sizeof(KERB_QUERY_TKT_CACHE_REQUEST)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
CacheRequest = (PKERB_QUERY_TKT_CACHE_REQUEST) ProtocolSubmitBuffer;
//
// Find the callers logon id & TCB status
//
Status = LsaFunctions->GetClientInfo(&ClientInfo); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// If the caller did not provide a logon id, use the caller's logon id.
//
if ( RtlIsZeroLuid( &CacheRequest->LogonId ) ) { LogonId = &ClientInfo.LogonId; } else { //
// Verify the caller has TCB privilege if they want access to someone
// elses ticket cache.
//
if (!ClientInfo.HasTcbPrivilege) { Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup; }
LogonId = &CacheRequest->LogonId; }
LogonSession = KerbReferenceLogonSession( LogonId, FALSE // don't unlink
);
if (LogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
//
// Now find the TGT from the authentication ticket cache.
//
KerbReadLockLogonSessions(LogonSession);
CacheEntry = KerbLocateTicketCacheEntryByRealm( &LogonSession->PrimaryCredentials.AuthenticationTicketCache, NULL, // get initial ticket
KERB_TICKET_CACHE_PRIMARY_TGT );
KerbUnlockLogonSessions(LogonSession);
if (CacheEntry == NULL) { Status = SEC_E_NO_CREDENTIALS; goto Cleanup; }
KerbReadLockTicketCache();
Status = KerbPackExternalTicket( CacheEntry, FALSE, ( ClientInfo.HasTcbPrivilege || KerbGlobalAllowTgtSessionKey ), &TicketSize, &ClientTicketResponse );
KerbUnlockTicketCache();
if (!NT_SUCCESS(Status)) { goto Cleanup; }
*ProtocolReturnBuffer = ClientTicketResponse; ClientTicketResponse = NULL; *ReturnBufferLength = TicketSize;
Cleanup: if (LogonSession != NULL) { KerbDereferenceLogonSession(LogonSession); } if (CacheEntry != NULL) { KerbDereferenceTicketCacheEntry(CacheEntry); } if (ClientTicketResponse != NULL) { LsaFunctions->FreeClientBuffer( NULL, ClientTicketResponse ); }
*ProtocolStatus = Status; return(STATUS_SUCCESS); }
//+-------------------------------------------------------------------------
//
// Function: KerbSetIpAddresses
//
// Synopsis: Saves the IP addresses passed in by netlogon
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbSetIpAddresses( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; PKERB_UPDATE_ADDRESSES_REQUEST UpdateRequest;
//
// This can only be called internally.
//
if (ClientRequest != NULL) { DebugLog((DEB_ERROR,"Can't update addresses from outside process. %ws, line %d\n", THIS_FILE, __LINE__)); Status = STATUS_ACCESS_DENIED; goto Cleanup; }
//
// Verify the request.
//
if (SubmitBufferLength < FIELD_OFFSET(KERB_UPDATE_ADDRESSES_REQUEST, Addresses)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
UpdateRequest = (PKERB_UPDATE_ADDRESSES_REQUEST) ProtocolSubmitBuffer;
//
// Validate the input
//
if (SubmitBufferLength < (sizeof(KERB_UPDATE_ADDRESSES_REQUEST) + UpdateRequest->AddressCount * (sizeof(SOCKET_ADDRESS) + sizeof(struct sockaddr_in)) - ANYSIZE_ARRAY * sizeof(ULONG))) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
Status= KerbUpdateGlobalAddresses( (PSOCKET_ADDRESS) UpdateRequest->Addresses, UpdateRequest->AddressCount );
//
// Copy them into the global for others to use
//
Cleanup:
*ProtocolStatus = Status; return(STATUS_SUCCESS); }
//+-------------------------------------------------------------------------
//
// Function: KerbVerifyPac
//
// Synopsis: Verifies that a PAC was signed by a valid KDC
//
// Effects:
//
// Arguments: Same as for LsaApCallAuthenticationPackage. The submit
// buffer must contain a KERB_VERIFY_PAC_REQUEST message.
//
// Requires:
//
// Returns: STATUS_SUCCESS. The real error is in the protocol status.
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbVerifyPac( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferLength, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; PKERB_VERIFY_PAC_REQUEST VerifyRequest; DWORD MaxBufferSize;
if (ARGUMENT_PRESENT(ProtocolReturnBuffer)) { *ProtocolReturnBuffer = NULL; } if (ARGUMENT_PRESENT(ReturnBufferLength)) { *ReturnBufferLength = 0; } if (SubmitBufferLength < sizeof(*VerifyRequest)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
if (!KerbGlobalInitialized) { Status = STATUS_SUCCESS; DsysAssert(FALSE); goto Cleanup; }
VerifyRequest = (PKERB_VERIFY_PAC_REQUEST) ProtocolSubmitBuffer;
MaxBufferSize = SubmitBufferLength - FIELD_OFFSET(KERB_VERIFY_PAC_REQUEST, ChecksumAndSignature); if ((VerifyRequest->ChecksumLength > MaxBufferSize) || (VerifyRequest->SignatureLength > MaxBufferSize) || ((VerifyRequest->ChecksumLength + VerifyRequest->SignatureLength) > MaxBufferSize)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
if (KerbKdcHandle == NULL) { Status = STATUS_MUST_BE_KDC; goto Cleanup; }
DsysAssert(KerbKdcVerifyPac != NULL);
Status = (*KerbKdcVerifyPac)( VerifyRequest->ChecksumLength, VerifyRequest->ChecksumAndSignature, VerifyRequest->SignatureType, VerifyRequest->SignatureLength, VerifyRequest->ChecksumAndSignature + VerifyRequest->ChecksumLength ); Cleanup: *ProtocolStatus = Status;
return(STATUS_SUCCESS); }
NTSTATUS KerbPurgePrimaryCredentialsTickets( IN KERB_PRIMARY_CREDENTIAL * PrimaryCredentials, IN OPTIONAL PUNICODE_STRING ServerName, IN OPTIONAL PUNICODE_STRING ServerRealm ) { NTSTATUS Status;
DsysAssert( PrimaryCredentials );
if ( ServerName == NULL && ServerRealm == NULL ) {
Status = STATUS_SUCCESS;
KerbPurgeTicketCache( &PrimaryCredentials->AuthenticationTicketCache ); KerbPurgeTicketCache( &PrimaryCredentials->ServerTicketCache ); KerbPurgeTicketCache( &PrimaryCredentials->S4UTicketCache );
} else if ( ServerName != NULL && ServerRealm != NULL ) {
KERB_TICKET_CACHE * TicketCaches[3] = { &PrimaryCredentials->AuthenticationTicketCache, &PrimaryCredentials->ServerTicketCache, &PrimaryCredentials->S4UTicketCache };
//
// Prowl through the caches and remove all the matching tickets
//
Status = STATUS_OBJECT_NAME_NOT_FOUND;
KerbWriteLockTicketCache();
for ( ULONG i = 0 ; i < 3 ; i++ ) {
KERB_TICKET_CACHE * TicketCache = TicketCaches[i];
for ( PLIST_ENTRY ListEntry = TicketCache->CacheEntries.Flink ; ListEntry != &TicketCache->CacheEntries ; ListEntry = ListEntry->Flink ) {
KERB_TICKET_CACHE_ENTRY * CacheEntry; UNICODE_STRING SearchName = {0};
CacheEntry= CONTAINING_RECORD( ListEntry, KERB_TICKET_CACHE_ENTRY, ListEntry.Next );
if ( !KERB_SUCCESS( KerbConvertKdcNameToString( &SearchName, CacheEntry->ServiceName, NULL ))) { // no realm
Status = STATUS_INSUFFICIENT_RESOURCES; KerbUnlockTicketCache(); goto Cleanup; }
//
// Check to see if the server & realm name matches
//
if ( RtlEqualUnicodeString( &SearchName, ServerName, TRUE ) && RtlEqualUnicodeString( &CacheEntry->DomainName, ServerRealm, TRUE )) {
D_DebugLog((DEB_TRACE,"Purging a ticket!\n"));
Status = STATUS_SUCCESS;
//
// Move back one entry so that Remove() does not
// trash the iteration
//
ListEntry = ListEntry->Blink;
KerbRemoveTicketCacheEntry( CacheEntry ); }
KerbFreeString(&SearchName); } }
KerbUnlockTicketCache();
} else {
//
// ServerName and ServerRealm need to be either both specified or
// both NULL. Getting here means that only one of them is NULL,
// and the assert below will specify which one it is.
//
DsysAssert( ServerName != NULL ); DsysAssert( ServerRealm != NULL );
Status = STATUS_SUCCESS; }
Cleanup:
return Status; }
//+-------------------------------------------------------------------------
//
// Function: KerbPurgeTicket
//
// Synopsis: Removes ticket from the ticket cache
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbPurgeTicket( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; ULONG StructureSize = sizeof( KERB_PURGE_TKT_CACHE_REQUEST ); PKERB_PURGE_TKT_CACHE_REQUEST PurgeRequest = ( PKERB_PURGE_TKT_CACHE_REQUEST )ProtocolSubmitBuffer; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonId; PKERB_LOGON_SESSION LogonSession = NULL;
//
// Verify the request.
//
D_DebugLog((DEB_TRACE, "Purging ticket cache\n"));
//
// Any purging will also tag SPN cache for purge
//
KerbCleanupSpnCache();
#if _WIN64
SECPKG_CALL_INFO CallInfo;
if(!LsaFunctions->GetCallInfo(&CallInfo)) { Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { StructureSize = sizeof( KERB_PURGE_TKT_CACHE_REQUEST_WOW64 ); }
#endif // _WIN64
if (SubmitBufferSize < StructureSize) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
#if _WIN64
KERB_PURGE_TKT_CACHE_REQUEST LocalPurgeRequest;
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { //
// Thunk 32-bit pointers if this is a WOW caller
//
PKERB_PURGE_TKT_CACHE_REQUEST_WOW64 PurgeRequestWOW = ( PKERB_PURGE_TKT_CACHE_REQUEST_WOW64 )PurgeRequest;
LocalPurgeRequest.MessageType = PurgeRequestWOW->MessageType; LocalPurgeRequest.LogonId = PurgeRequestWOW->LogonId;
UNICODE_STRING_FROM_WOW_STRING( &LocalPurgeRequest.ServerName, &PurgeRequestWOW->ServerName );
UNICODE_STRING_FROM_WOW_STRING( &LocalPurgeRequest.RealmName, &PurgeRequestWOW->RealmName );
PurgeRequest = &LocalPurgeRequest; }
#endif // _WIN64
//
// Normalize the strings
//
NULL_RELOCATE_ONE( &PurgeRequest->ServerName ); NULL_RELOCATE_ONE( &PurgeRequest->RealmName );
//
// Find the callers logon id & TCB status
//
Status = LsaFunctions->GetClientInfo( &ClientInfo );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// If the caller did not provide a logon id, use the caller's logon id.
//
if ( RtlIsZeroLuid( &PurgeRequest->LogonId )) {
LogonId = &ClientInfo.LogonId;
} else if ( !ClientInfo.HasTcbPrivilege ) {
//
// The caller must have TCB privilege in order to access someone
// else's ticket cache.
//
Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup;
} else {
LogonId = &PurgeRequest->LogonId; }
LogonSession = KerbReferenceLogonSession( LogonId, FALSE // don't unlink
);
if (LogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
//
// If no servername / realm name were supplied, purge all tickets
//
if ((PurgeRequest->ServerName.Length) == 0 && (PurgeRequest->RealmName.Length == 0)) { D_DebugLog((DEB_TRACE, "Purging all tickets\n"));
Status = KerbPurgePrimaryCredentialsTickets( &LogonSession->PrimaryCredentials, NULL, NULL );
} else {
D_DebugLog(( DEB_TRACE, "Purging tickets %wZ\\%wZ\n", &PurgeRequest->RealmName, &PurgeRequest->ServerName ));
Status = KerbPurgePrimaryCredentialsTickets( &LogonSession->PrimaryCredentials, &PurgeRequest->ServerName, &PurgeRequest->RealmName ); }
Cleanup:
if (LogonSession != NULL) { KerbDereferenceLogonSession(LogonSession); }
*ProtocolReturnBuffer = NULL; *ReturnBufferLength = 0;
*ProtocolStatus = Status; return STATUS_SUCCESS; }
//+-------------------------------------------------------------------------
//
// Function: KerbPurgeTicketEx
//
// Synopsis: Removes ticket from the ticket cache
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbPurgeTicketEx( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; ULONG StructureSize = sizeof( KERB_PURGE_TKT_CACHE_EX_REQUEST ); PKERB_PURGE_TKT_CACHE_EX_REQUEST PurgeRequest = ( PKERB_PURGE_TKT_CACHE_EX_REQUEST )ProtocolSubmitBuffer; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonId; PKERB_LOGON_SESSION LogonSession = NULL;
//
// Verify the request.
//
D_DebugLog((DEB_TRACE, "Purging ticket cache Ex\n"));
//
// Any purging will also tag SPN cache for purge
//
KerbCleanupSpnCache();
#if _WIN64
SECPKG_CALL_INFO CallInfo;
if( !LsaFunctions->GetCallInfo( &CallInfo )) {
Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) {
StructureSize = sizeof( KERB_PURGE_TKT_CACHE_EX_REQUEST_WOW64 ); }
#endif
if ( SubmitBufferSize < StructureSize ) {
Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
#if _WIN64
KERB_PURGE_TKT_CACHE_EX_REQUEST LocalPurgeRequest;
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) {
//
// Thunk 32-bit pointers if this is a WOW caller
//
PKERB_PURGE_TKT_CACHE_EX_REQUEST_WOW64 PurgeRequestWOW = ( PKERB_PURGE_TKT_CACHE_EX_REQUEST_WOW64 )PurgeRequest;
LocalPurgeRequest.MessageType = PurgeRequestWOW->MessageType; LocalPurgeRequest.LogonId = PurgeRequestWOW->LogonId;
UNICODE_STRING_FROM_WOW_STRING( &LocalPurgeRequest.TicketTemplate.ClientName, &PurgeRequestWOW->TicketTemplate.ClientName );
UNICODE_STRING_FROM_WOW_STRING( &LocalPurgeRequest.TicketTemplate.ClientRealm, &PurgeRequestWOW->TicketTemplate.ClientRealm );
UNICODE_STRING_FROM_WOW_STRING( &LocalPurgeRequest.TicketTemplate.ServerName, &PurgeRequestWOW->TicketTemplate.ServerName );
UNICODE_STRING_FROM_WOW_STRING( &LocalPurgeRequest.TicketTemplate.ServerRealm, &PurgeRequestWOW->TicketTemplate.ServerRealm );
LocalPurgeRequest.TicketTemplate.StartTime = PurgeRequestWOW->TicketTemplate.StartTime; LocalPurgeRequest.TicketTemplate.EndTime = PurgeRequestWOW->TicketTemplate.EndTime; LocalPurgeRequest.TicketTemplate.RenewTime = PurgeRequestWOW->TicketTemplate.RenewTime; LocalPurgeRequest.TicketTemplate.EncryptionType = PurgeRequestWOW->TicketTemplate.EncryptionType; LocalPurgeRequest.TicketTemplate.TicketFlags = PurgeRequestWOW->TicketTemplate.TicketFlags;
PurgeRequest = &LocalPurgeRequest; }
#endif
//
// Normalize the strings
//
NULL_RELOCATE_ONE( &PurgeRequest->TicketTemplate.ClientName ); NULL_RELOCATE_ONE( &PurgeRequest->TicketTemplate.ClientRealm ); NULL_RELOCATE_ONE( &PurgeRequest->TicketTemplate.ServerName ); NULL_RELOCATE_ONE( &PurgeRequest->TicketTemplate.ServerRealm );
//
// Find the callers logon id & TCB status
//
Status = LsaFunctions->GetClientInfo( &ClientInfo );
if ( !NT_SUCCESS( Status )) {
goto Cleanup; }
//
// If the caller did not provide a logon id, use the caller's logon id
//
if ( RtlIsZeroLuid( &PurgeRequest->LogonId )) {
LogonId = &ClientInfo.LogonId;
} else if ( !ClientInfo.HasTcbPrivilege ) {
//
// The caller is required to have the TCB privilege
// in order to access someone else's ticket cache
//
Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup;
} else {
LogonId = &PurgeRequest->LogonId; }
LogonSession = KerbReferenceLogonSession( LogonId, FALSE );
if ( LogonSession == NULL ) {
Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
//
// Purge the entire ticket cache?
//
if ( PurgeRequest->Flags & KERB_PURGE_ALL_TICKETS ) {
D_DebugLog(( DEB_TRACE, "Purging all tickets\n" ));
Status = KerbPurgePrimaryCredentialsTickets( &LogonSession->PrimaryCredentials, NULL, NULL );
DsysAssert( NT_SUCCESS( Status ));
KerbLockList( &LogonSession->CredmanCredentials );
for ( PLIST_ENTRY ListEntry = LogonSession->CredmanCredentials.List.Flink; ListEntry != &LogonSession->CredmanCredentials.List; ListEntry = ListEntry->Flink ) {
PKERB_CREDMAN_CRED CredmanCred = CONTAINING_RECORD( ListEntry, KERB_CREDMAN_CRED, ListEntry.Next );
if ( CredmanCred->SuppliedCredentials == NULL) {
continue; }
Status = KerbPurgePrimaryCredentialsTickets( CredmanCred->SuppliedCredentials, NULL, NULL );
DsysAssert( NT_SUCCESS( Status )); }
KerbUnlockList( &LogonSession->CredmanCredentials );
} else {
BOOLEAN MatchClient = ( PurgeRequest->TicketTemplate.ClientName.Length > 0 || PurgeRequest->TicketTemplate.ClientRealm.Length > 0 );
BOOLEAN MatchServer = ( PurgeRequest->TicketTemplate.ServerName.Length > 0 || PurgeRequest->TicketTemplate.ServerRealm.Length > 0 );
BOOLEAN Found = FALSE;
//
// Take a look at the primary credentials and see if they need cleaning
//
if ( !MatchClient || ( RtlEqualUnicodeString( &LogonSession->PrimaryCredentials.UserName, &PurgeRequest->TicketTemplate.ClientName, TRUE ) && RtlEqualUnicodeString( &LogonSession->PrimaryCredentials.DomainName, &PurgeRequest->TicketTemplate.ClientRealm, TRUE ))) {
UNICODE_STRING * MatchServerName; UNICODE_STRING * MatchServerRealm;
if ( MatchServer ) {
MatchServerName = &PurgeRequest->TicketTemplate.ServerName; MatchServerRealm = &PurgeRequest->TicketTemplate.ServerRealm;
} else {
MatchServerName = NULL; MatchServerRealm = NULL; }
Status = KerbPurgePrimaryCredentialsTickets( &LogonSession->PrimaryCredentials, MatchServerName, MatchServerRealm );
if ( NT_SUCCESS( Status )) {
Found = TRUE;
} else if ( Status != STATUS_OBJECT_NAME_NOT_FOUND ) {
goto Cleanup; } }
//
// Now look at the credman credentials and purge those
//
KerbLockList( &LogonSession->CredmanCredentials );
for ( PLIST_ENTRY ListEntry = LogonSession->CredmanCredentials.List.Flink; ListEntry != &LogonSession->CredmanCredentials.List; ListEntry = ListEntry->Flink ) {
PKERB_CREDMAN_CRED CredmanCred = CONTAINING_RECORD( ListEntry, KERB_CREDMAN_CRED, ListEntry.Next );
if ( CredmanCred->SuppliedCredentials == NULL ) {
continue; }
if ( !MatchClient || ( RtlEqualUnicodeString( &CredmanCred->SuppliedCredentials->UserName, &PurgeRequest->TicketTemplate.ClientName, TRUE ) && RtlEqualUnicodeString( &CredmanCred->SuppliedCredentials->DomainName, &PurgeRequest->TicketTemplate.ClientRealm, TRUE ))) {
UNICODE_STRING * MatchServerName; UNICODE_STRING * MatchServerRealm;
if ( MatchServer ) {
MatchServerName = &PurgeRequest->TicketTemplate.ServerName; MatchServerRealm = &PurgeRequest->TicketTemplate.ServerRealm;
} else {
MatchServerName = NULL; MatchServerRealm = NULL; }
Status = KerbPurgePrimaryCredentialsTickets( CredmanCred->SuppliedCredentials, MatchServerName, MatchServerRealm );
if ( NT_SUCCESS( Status )) {
Found = TRUE;
} else if ( Status != STATUS_OBJECT_NAME_NOT_FOUND ) {
KerbUnlockList( &LogonSession->CredmanCredentials ); goto Cleanup; } } }
KerbUnlockList( &LogonSession->CredmanCredentials );
if ( Found ) {
Status = STATUS_SUCCESS;
} else {
Status = STATUS_OBJECT_NAME_NOT_FOUND; } }
Cleanup:
if ( LogonSession ) {
KerbDereferenceLogonSession( LogonSession ); }
*ProtocolReturnBuffer = NULL; *ReturnBufferLength = NULL;
*ProtocolStatus = Status; return STATUS_SUCCESS; }
//+-------------------------------------------------------------------------
//
// Function: KerbRetrieveEncodedTicket
//
// Synopsis: Retrieves an asn.1 encoded ticket from the ticket cache
// specified.
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbRetrieveEncodedTicket( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; SECPKG_CLIENT_INFO ClientInfo; LUID DummyLogonId, *LogonId; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_CREDENTIAL Credential = NULL; KERB_PRIMARY_CREDENTIAL * PrimaryCreds = NULL; PKERB_RETRIEVE_TKT_REQUEST RetrieveRequest = ( PKERB_RETRIEVE_TKT_REQUEST )ProtocolSubmitBuffer; PKERB_RETRIEVE_TKT_RESPONSE RetrieveResponse = NULL; KERB_TICKET_CACHE_ENTRY * CacheEntry = NULL; PBYTE ClientResponse = NULL; ULONG ResponseSize; PKERB_INTERNAL_NAME TargetName = NULL; UNICODE_STRING TargetRealm = {0}; ULONG Flags = 0; ULONG StructureSize = sizeof( KERB_RETRIEVE_TKT_REQUEST );
//
// Verify the request.
//
D_DebugLog(( DEB_TRACE, "Retrieving encoded ticket\n" ));
#if _WIN64
SECPKG_CALL_INFO CallInfo;
//
// Return 32-bit cache entries if this is a WOW caller
//
if (!LsaFunctions->GetCallInfo(&CallInfo)) {
Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT) { Status = STATUS_NOT_SUPPORTED; goto Cleanup; }
#endif // _WIN64
if (SubmitBufferSize < StructureSize) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
//
// Normalize the strings
//
NULL_RELOCATE_ONE( &RetrieveRequest->TargetName );
//
// Find the callers logon id & TCB status
//
Status = LsaFunctions->GetClientInfo( &ClientInfo );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// If the caller did not provide a logon id, use the caller's logon id.
//
if ( (RetrieveRequest->CacheOptions & KERB_RETRIEVE_TICKET_USE_CREDHANDLE) != 0) { //
// Get the associated credential
//
Status = KerbReferenceCredential( RetrieveRequest->CredentialsHandle.dwUpper, KERB_CRED_OUTBOUND | KERB_CRED_TGT_AVAIL, FALSE, &Credential);
if (!NT_SUCCESS(Status)) { DebugLog(( DEB_WARN, "Failed to locate credential: 0x%x\n", Status )); goto Cleanup; }
//
// Get the logon id from the credentials so we can locate the
// logon session.
//
DummyLogonId = Credential->LogonId; LogonId = &DummyLogonId;
} else if ( RtlIsZeroLuid( &RetrieveRequest->LogonId ) ) {
LogonId = &ClientInfo.LogonId;
} else if ( !ClientInfo.HasTcbPrivilege ) { //
// The caller must have TCB privilege in order to access someone
// elses ticket cache.
//
Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup;
} else {
LogonId = &RetrieveRequest->LogonId; }
LogonSession = KerbReferenceLogonSession( LogonId, FALSE // don't unlink
);
if (LogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
//
// Process the target names
//
Status = KerbProcessTargetNames( &RetrieveRequest->TargetName, NULL, // no supp target name
0, // no flags
&Flags, &TargetName, &TargetRealm );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Check the TGT cache, as KerbGetServiceTicket doesn't look there
//
if ((RetrieveRequest->CacheOptions & KERB_RETRIEVE_TICKET_DONT_USE_CACHE) == 0) { KerbReadLockLogonSessions(LogonSession);
//
// Pick which ticket cache to use
//
if ((Credential != NULL) && (Credential->SuppliedCredentials != NULL)) { PrimaryCreds = Credential->SuppliedCredentials; } else { PrimaryCreds = &LogonSession->PrimaryCredentials; }
CacheEntry = KerbLocateTicketCacheEntry( &PrimaryCreds->AuthenticationTicketCache, TargetName, &TargetRealm );
if (CacheEntry == NULL) { //
// If the tgt cache failed, check the normal cache
//
CacheEntry = KerbLocateTicketCacheEntry( &PrimaryCreds->ServerTicketCache, TargetName, &TargetRealm ); }
//
// Check if this is a TGT
//
if (CacheEntry == NULL) { if ((TargetName->NameCount == 2) && RtlEqualUnicodeString( &TargetName->Names[0], &KerbGlobalKdcServiceName, TRUE // case insensitive
)) {
//
// If the tgt cache failed, check the normal cache
//
CacheEntry = KerbLocateTicketCacheEntryByRealm( &PrimaryCreds->AuthenticationTicketCache, &TargetRealm, KERB_TICKET_CACHE_PRIMARY_TGT );
if (CacheEntry != NULL) { //
// Make sure the name matches
//
KerbReadLockTicketCache();
if ( !KerbEqualKdcNames( TargetName, CacheEntry->ServiceName )) { //
// We must unlock the ticket cache before dereferencing
//
KerbUnlockTicketCache(); KerbDereferenceTicketCacheEntry( CacheEntry ); CacheEntry = NULL;
} else { KerbUnlockTicketCache(); } } } }
//
// If we found a ticket, make sure it has the right flags &
// encryption type
//
if (CacheEntry != NULL) { ULONG TicketFlags; ULONG CacheTicketFlags; LONG CacheEncryptionType;
//
// Check if the flags are present
//
KerbReadLockTicketCache(); CacheTicketFlags = CacheEntry->TicketFlags; CacheEncryptionType = CacheEntry->Ticket.encrypted_part.encryption_type; KerbUnlockTicketCache();
TicketFlags = KerbConvertKdcOptionsToTicketFlags( RetrieveRequest->TicketFlags );
//
// Verify the flags
//
if ((( CacheTicketFlags & TicketFlags ) != TicketFlags) || ((RetrieveRequest->EncryptionType != KERB_ETYPE_DEFAULT) && (CacheEncryptionType != RetrieveRequest->EncryptionType))) { //
// Something doesn't match, so throw away the entry
//
KerbDereferenceTicketCacheEntry( CacheEntry ); CacheEntry = NULL; } }
KerbUnlockLogonSessions(LogonSession); } else { Flags |= KERB_GET_TICKET_NO_CACHE; }
if (CacheEntry == NULL) { //
// If we aren't supposed to get a new ticket, return a failure now.
//
if ((RetrieveRequest->CacheOptions & KERB_RETRIEVE_TICKET_USE_CACHE_ONLY) != 0) { Status = STATUS_OBJECT_NAME_NOT_FOUND; goto Cleanup; }
//
// Now get a ticket
//
Status = KerbGetServiceTicket( LogonSession, Credential, NULL, TargetName, &TargetRealm, Flags, RetrieveRequest->TicketFlags, RetrieveRequest->EncryptionType, NULL, // no error message
NULL, // no authorization data
NULL, // no tgt reply
&CacheEntry, NULL // don't return logon guid
);
if (!NT_SUCCESS(Status)) { DebugLog((DEB_WARN, "KerbRetrieveEncodedTicket failed to get outbound ticket: KerbGetServiceTicket failed with 0x%x\n",Status)); goto Cleanup; } }
//
// Encode the ticket or kerb_cred
//
KerbReadLockTicketCache();
Status = KerbPackExternalTicket( CacheEntry, RetrieveRequest->CacheOptions & KERB_RETRIEVE_TICKET_AS_KERB_CRED, ( ClientInfo.HasTcbPrivilege || KerbGlobalAllowTgtSessionKey ), &ResponseSize, &ClientResponse );
KerbUnlockTicketCache();
if (!NT_SUCCESS(Status)) { goto Cleanup; }
*ProtocolReturnBuffer = ClientResponse; ClientResponse = NULL; *ReturnBufferLength = ResponseSize;
Cleanup:
if (CacheEntry != NULL) { KerbDereferenceTicketCacheEntry( CacheEntry ); }
if (LogonSession != NULL) { KerbDereferenceLogonSession( LogonSession ); }
if (Credential != NULL) { KerbDereferenceCredential( Credential ); }
KerbFree( RetrieveResponse );
if (ClientResponse != NULL) { LsaFunctions->FreeClientBuffer( NULL, ClientResponse ); }
KerbFreeString( &TargetRealm ); KerbFreeKdcName( &TargetName );
*ProtocolStatus = Status; return STATUS_SUCCESS; }
#if 0
//+-------------------------------------------------------------------------
//
// Function: KerbRetrieveEncodedTicketEx
//
// Synopsis: Retrieves an asn.1 encoded ticket from the ticket cache
// specified.
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbRetrieveEncodedTicketEx( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; SECPKG_CLIENT_INFO ClientInfo; ULONG StructureSize = sizeof( KERB_RETRIEVE_TKT_EX_REQUEST ); PKERB_RETRIEVE_TKT_EX_REQUEST RetrieveRequest = ( PKERB_RETRIEVE_TKT_EX_REQUEST )ProtocolSubmitBuffer; PKERB_RETRIEVE_TKT_EX_RESPONSE RetrieveResponse = NULL; PKERB_CREDENTIAL Credential = NULL; LUID DummyLogonId, *LogonId; PKERB_LOGON_SESSION LogonSession = NULL; ULONG Flags = 0; PKERB_INTERNAL_NAME TargetName = NULL; UNICODE_STRING TargetRealm = {0}; PBYTE ClientResponse = NULL; ULONG ResponseSize;
//
// Verify the request
//
D_DebugLog(( DEB_TRACE, "Retrieving encoded ticket ex\n" ));
#if _WIN64
SECPKG_CALL_INFO CallInfo;
//
// Return 32-bit cache entries if this is a WOW caller
//
if( !LsaFunctions->GetCallInfo( &CallInfo )) {
Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) {
StructureSize = sizeof( KERB_RETRIEVE_TKT_EX_REQUEST_WOW64 ); }
#endif // _WIN64
if ( SubmitBufferSize < StructureSize ) {
Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
#if _WIN64
KERB_RETRIEVE_TKT_EX_REQUEST LocalRetrieveRequest;
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) {
//
// Thunk 32-bit pointers if this is a WOW caller
//
PKERB_RETRIEVE_TKT_EX_REQUEST_WOW64 RetrieveRequestWOW = ( PKERB_RETRIEVE_TKT_EX_REQUEST_WOW64 )RetrieveRequest;
LocalRetrieveRequest.MessageType = RetrieveRequestWOW->MessageType; LocalRetrieveRequest.LogonId = RetrieveRequestWOW->LogonId;
UNICODE_STRING_FROM_WOW_STRING( &LocalRetrieveRequest.TicketTemplate.ClientName, &RetrieveRequestWOW->TicketTemplate.ClientName );
UNICODE_STRING_FROM_WOW_STRING( &LocalRetrieveRequest.TicketTemplate.ClientRealm, &RetrieveRequestWOW->TicketTemplate.ClientRealm );
UNICODE_STRING_FROM_WOW_STRING( &LocalRetrieveRequest.TicketTemplate.ServerName, &RetrieveRequestWOW->TicketTemplate.ServerName );
UNICODE_STRING_FROM_WOW_STRING( &LocalRetrieveRequest.TicketTemplate.ServerRealm, &RetrieveRequestWOW->TicketTemplate.ServerRealm );
LocalRetrieveRequest.TicketTemplate.StartTime = RetrieveRequestWOW->TicketTemplate.StartTime; LocalRetrieveRequest.TicketTemplate.EndTime = RetrieveRequestWOW->TicketTemplate.EndTime; LocalRetrieveRequest.TicketTemplate.RenewTime = RetrieveRequestWOW->TicketTemplate.RenewTime; LocalRetrieveRequest.TicketTemplate.EncryptionType = RetrieveRequestWOW->TicketTemplate.EncryptionType; LocalRetrieveRequest.TicketTemplate.TicketFlags = RetrieveRequestWOW->TicketTemplate.TicketFlags;
LocalRetrieveRequest.CacheOptions = RetrieveRequestWOW->CacheOptions; LocalRetrieveRequest.CredentialsHandle = RetrieveRequestWOW->CredentialsHandle;
//
// TODO: take care of SecondTicket, UserAuthData and Addresses
//
LocalRetrieveRequest.SecondTicket = NULL; LocalRetrieveRequest.UserAuthData = NULL; LocalRetrieveRequest.Addresses = NULL;
RetrieveRequest = &LocalRetrieveRequest; }
#endif
//
// Normalize the strings
//
NULL_RELOCATE_ONE( &RetrieveRequest->TicketTemplate.ClientName ); NULL_RELOCATE_ONE( &RetrieveRequest->TicketTemplate.ClientRealm ); NULL_RELOCATE_ONE( &RetrieveRequest->TicketTemplate.ServerName ); NULL_RELOCATE_ONE( &RetrieveRequest->TicketTemplate.ServerRealm );
//
// TODO: take care of SecondTicket, UserAuthData and Addresses
//
if ( RetrieveRequest->SecondTicket != NULL || RetrieveRequest->UserAuthData != NULL || RetrieveRequest->Addresses != NULL ) {
Status = STATUS_NOT_SUPPORTED; goto Cleanup; }
//
// Find the callers logon id & TCB status
//
Status = LsaFunctions->GetClientInfo( &ClientInfo );
if ( !NT_SUCCESS( Status )) {
goto Cleanup; }
//
// If the caller did not provide a logon id, user the caller's logon id
//
if ( RetrieveRequest->CacheOptions & KERB_RETRIEVE_TICKET_USE_CREDHANDLE ) {
//
// Get the associated credential
//
Status = KerbReferenceCredential( RetrieveRequest->CredentialsHandle.dwUpper, KERB_CRED_OUTBOUND | KERB_CRED_TGT_AVAIL, FALSE, &Credential );
if ( !NT_SUCCESS( Status )) {
DebugLog(( DEB_WARN, "Failed to locate credential: 0x%x\n", Status )); goto Cleanup; }
//
// Get the logon id from the credentials so we can locate the logon session
//
DummyLogonId = Credential->LogonId; LogonId = &DummyLogonId;
} else if ( RtlIsZeroLuid( &RetrieveRequest->LogonId )) {
LogonId = &ClientInfo.LogonId;
} else if ( !ClientInfo.HasTcbPrivilege ) {
//
// The caller must have TCB privilege in order to access someone else's
// ticket cache
//
Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup;
} else {
LogonId = &RetrieveRequest->LogonId; }
LogonSession = KerbReferenceLogonSession( LogonId, FALSE );
if ( LogonSession == NULL ) {
Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
*ProtocolReturnBuffer = ClientResponse; ClientResponse = NULL; *ReturnBufferLength = ResponseSize;
Cleanup:
if ( LogonSession != NULL ) {
KerbDereferenceLogonSession( LogonSession ); }
if ( Credential != NULL ) {
KerbDereferenceCredential( Credential ); }
KerbFree( RetrieveResponse );
if ( ClientResponse != NULL ) {
LsaFunctions->FreeClientBuffer( NULL, ClientResponse ); }
*ProtocolStatus = Status; return STATUS_SUCCESS; }
#endif // 0
//+-------------------------------------------------------------------------
//
// Function: KerbDecryptMessage
//
// Synopsis: Decrypts a buffer with either the specified key or the d
// primary key from the specified logon session.
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbDecryptMessage( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonId; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_DECRYPT_REQUEST DecryptRequest; PBYTE DecryptResponse = NULL; ULONG ResponseLength = 0;
PBYTE ClientResponse = NULL; PKERB_ENCRYPTION_KEY KeyToUse = NULL; KERB_ENCRYPTION_KEY SuppliedKey = {0}; BOOLEAN FreeKey = FALSE; PCRYPTO_SYSTEM CryptoSystem = NULL; PCRYPT_STATE_BUFFER CryptBuffer = NULL;
//
// Verify the request.
//
D_DebugLog((DEB_TRACE, "Decrypting Message\n"));
if (SubmitBufferSize < sizeof(KERB_DECRYPT_REQUEST)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
DecryptRequest = (PKERB_DECRYPT_REQUEST) ProtocolSubmitBuffer;
//
// Validate the pointers
//
if (DecryptRequest->InitialVector != NULL) { if (DecryptRequest->InitialVector - (PUCHAR) ClientBufferBase + DecryptRequest->InitialVectorSize > SubmitBufferSize) { DebugLog((DEB_ERROR,"InitialVector end pass end of buffer\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
if (DecryptRequest->InitialVector < (PUCHAR) ClientBufferBase + sizeof(KERB_DECRYPT_REQUEST)) { DebugLog((DEB_ERROR,"InitialVector begin before end of DECRYPT_REQUEST\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } DecryptRequest->InitialVector = DecryptRequest->InitialVector - (PUCHAR) ClientBufferBase + (PUCHAR) ProtocolSubmitBuffer; } else { if (DecryptRequest->InitialVectorSize != 0) { DebugLog((DEB_ERROR,"Non-zero vector size with null vector\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } }
if (DecryptRequest->EncryptedData - (PUCHAR) ClientBufferBase + DecryptRequest->EncryptedDataSize > SubmitBufferSize) { DebugLog((DEB_ERROR,"EncryptedData end past end of request buffer\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
if (DecryptRequest->EncryptedData < (PUCHAR) ClientBufferBase + sizeof(KERB_DECRYPT_REQUEST)) { DebugLog((DEB_ERROR,"EncryptedData begin before end of DECRYPT_REQUEST\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } DecryptRequest->EncryptedData = DecryptRequest->EncryptedData - (PUCHAR) ClientBufferBase + (PUCHAR) ProtocolSubmitBuffer;
//
// If the caller wants the default key, then open the specified logon
// session and get out the key.
//
if (DecryptRequest->Flags & KERB_DECRYPT_FLAG_DEFAULT_KEY) { //
// Find the callers logon id & TCB status
//
Status = LsaFunctions->GetClientInfo(&ClientInfo); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// If the caller did not provide a logon id, use the caller's logon id.
//
if ( RtlIsZeroLuid( &DecryptRequest->LogonId ) ) { LogonId = &ClientInfo.LogonId; } else { //
// Verify the caller has TCB privilege if they want access to someone
// elses ticket cache.
//
if (!ClientInfo.HasTcbPrivilege) { Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup; }
LogonId = &DecryptRequest->LogonId; }
LogonSession = KerbReferenceLogonSession( LogonId, FALSE // don't unlink
);
if (LogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; }
//
// Get the key from the logon session
//
KerbReadLockLogonSessions(LogonSession); if (LogonSession->PrimaryCredentials.Passwords != NULL) { KeyToUse = KerbGetKeyFromList( LogonSession->PrimaryCredentials.Passwords, DecryptRequest->CryptoType ); if (KeyToUse != NULL) { KERBERR KerbErr;
KerbErr = KerbDuplicateKey( &SuppliedKey, KeyToUse ); KeyToUse = NULL; Status = KerbMapKerbError(KerbErr); } else { Status = STATUS_OBJECT_NAME_NOT_FOUND; }
} else { Status = STATUS_OBJECT_NAME_NOT_FOUND; } KerbUnlockLogonSessions(LogonSession); if (!NT_SUCCESS(Status)) { goto Cleanup; } KeyToUse = &SuppliedKey; FreeKey = TRUE; } else { if (DecryptRequest->Key.Value - (PUCHAR) ClientBufferBase + DecryptRequest->Key.Length > SubmitBufferSize) { DebugLog((DEB_ERROR,"End of supplied key past end of request buffer\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
if (DecryptRequest->Key.Value < (PUCHAR) ClientBufferBase + sizeof(KERB_DECRYPT_REQUEST)) { DebugLog((DEB_ERROR,"Begin of supplied key before end of DECRYPT_REQUEST\n")); Status = STATUS_INVALID_PARAMETER; goto Cleanup; } DecryptRequest->Key.Value = DecryptRequest->Key.Value - (PUCHAR) ClientBufferBase + (PUCHAR) ProtocolSubmitBuffer; SuppliedKey.keytype = DecryptRequest->Key.KeyType; SuppliedKey.keyvalue.value = DecryptRequest->Key.Value; SuppliedKey.keyvalue.length = DecryptRequest->Key.Length; KeyToUse = &SuppliedKey; }
//
// Now do the decryption
//
SafeAllocaAllocate(DecryptResponse, DecryptRequest->EncryptedDataSize);
if (DecryptResponse == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
ResponseLength = DecryptRequest->EncryptedDataSize;
Status = CDLocateCSystem( DecryptRequest->CryptoType, &CryptoSystem );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// The crypt system must be integrity protected - otherwise it may be
// used as a general purpose encryption/decryption technique.
//
if ((CryptoSystem->Attributes & CSYSTEM_INTEGRITY_PROTECTED) == 0) { DebugLog((DEB_ERROR,"Trying to decrypt with non-integrity protected crypt system (%d)\n", CryptoSystem->EncryptionType)); Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
Status = CryptoSystem->Initialize( KeyToUse->keyvalue.value, KeyToUse->keyvalue.length, DecryptRequest->KeyUsage, &CryptBuffer ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// If there was an initial vector, use it now
//
if (DecryptRequest->InitialVectorSize != 0) { Status = CryptoSystem->Control( CRYPT_CONTROL_SET_INIT_VECT, CryptBuffer, DecryptRequest->InitialVector, DecryptRequest->InitialVectorSize ); if (!NT_SUCCESS(Status)) { goto Cleanup; } }
//
// Decrypt
//
Status = CryptoSystem->Decrypt( CryptBuffer, DecryptRequest->EncryptedData, DecryptRequest->EncryptedDataSize, DecryptResponse, &ResponseLength ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
//
// Return the decrypted data to the client
//
Status = LsaFunctions->AllocateClientBuffer( NULL, ResponseLength, (PVOID *) &ClientResponse ); if (!NT_SUCCESS(Status)) { goto Cleanup; }
Status = LsaFunctions->CopyToClientBuffer( NULL, ResponseLength, ClientResponse, DecryptResponse ); if (!NT_SUCCESS(Status)) { goto Cleanup; } *ProtocolReturnBuffer = ClientResponse; ClientResponse = NULL; *ReturnBufferLength = ResponseLength;
Cleanup:
if ((CryptoSystem != NULL) && (CryptBuffer != NULL)) { CryptoSystem->Discard(&CryptBuffer); } if (FreeKey) { KerbFreeKey(&SuppliedKey); }
if (LogonSession != NULL) { KerbDereferenceLogonSession(LogonSession); }
SafeAllocaFree(DecryptResponse);
if (ClientResponse != NULL) { LsaFunctions->FreeClientBuffer( NULL, ClientResponse ); }
*ProtocolStatus = Status;
return(STATUS_SUCCESS); }
//+-------------------------------------------------------------------------
//
// Function: KerbAddBindingCacheEntry
//
// Synopsis: Adds an entry to the binding cache
//
// Effects:
//
// Arguments: Same as Callpackage
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI KerbAddBindingCacheEntry( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status; SECPKG_CLIENT_INFO ClientInfo; PKERB_ADD_BINDING_CACHE_ENTRY_REQUEST BindingRequest = ( PKERB_ADD_BINDING_CACHE_ENTRY_REQUEST )ProtocolSubmitBuffer; PKERB_BINDING_CACHE_ENTRY CacheEntry = NULL; ULONG StructureSize = sizeof( KERB_ADD_BINDING_CACHE_ENTRY_REQUEST );
//
// Verify the request.
//
D_DebugLog(( DEB_TRACE, "Addding binding cache entry\n" ));
#if _WIN64
SECPKG_CALL_INFO CallInfo;
//
// Return 32-bit cache entries if this is a WOW caller
//
if(!LsaFunctions->GetCallInfo( &CallInfo )) { Status = STATUS_INTERNAL_ERROR; goto Cleanup; }
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) {
StructureSize = sizeof( KERB_ADD_BINDING_CACHE_ENTRY_REQUEST_WOW64 ); }
#endif // _WIN64
if ( SubmitBufferSize < StructureSize ) {
Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
#if _WIN64
KERB_ADD_BINDING_CACHE_ENTRY_REQUEST LocalBindingRequest;
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) {
//
// Thunk 32-bit pointers if this is a WOW caller
//
PKERB_ADD_BINDING_CACHE_ENTRY_REQUEST_WOW64 BindingRequestWOW = ( PKERB_ADD_BINDING_CACHE_ENTRY_REQUEST_WOW64 )BindingRequest;
LocalBindingRequest.MessageType = BindingRequestWOW->MessageType;
UNICODE_STRING_FROM_WOW_STRING( &LocalBindingRequest.RealmName, &BindingRequestWOW->RealmName );
UNICODE_STRING_FROM_WOW_STRING( &LocalBindingRequest.KdcAddress, &BindingRequestWOW->KdcAddress );
LocalBindingRequest.AddressType = BindingRequestWOW->AddressType;
BindingRequest = &LocalBindingRequest; }
#endif // _WIN64
//
// Normalize the strings
//
NULL_RELOCATE_ONE( &BindingRequest->RealmName ); NULL_RELOCATE_ONE( &BindingRequest->KdcAddress );
//
// Find the callers logon id & TCB status
//
Status = LsaFunctions->GetClientInfo( &ClientInfo );
if ( !NT_SUCCESS( Status )) {
goto Cleanup; }
//
// Require the caller to have TCB.
//
if ( !ClientInfo.HasTcbPrivilege ) {
Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup; }
Status = KerbCacheBinding( &BindingRequest->RealmName, &BindingRequest->KdcAddress, BindingRequest->AddressType, 0, 0, 0, &CacheEntry );
Cleanup:
if ( CacheEntry != NULL ) {
KerbDereferenceBindingCacheEntry( CacheEntry ); }
*ProtocolReturnBuffer = NULL; *ReturnBufferLength = 0; *ProtocolStatus = Status;
return STATUS_SUCCESS; }
NTSTATUS VerifyCredentials( IN PUNICODE_STRING UserName, IN PUNICODE_STRING DomainName, IN PUNICODE_STRING Password ) { SAMPR_HANDLE UserHandle = NULL; PSAMPR_USER_INFO_BUFFER UserAllInfo = NULL; PSAMPR_USER_ALL_INFORMATION UserAll; SID_AND_ATTRIBUTES_LIST GroupMembership;
NT_OWF_PASSWORD NtOwfPassword; BOOLEAN UpdateLogonStats = FALSE; NTSTATUS Status = STATUS_LOGON_FAILURE;
GroupMembership.SidAndAttributes = NULL;
//
// lazy initialization of SAM handles.
//
if( KerbGlobalDomainHandle == NULL ) { SAMPR_HANDLE SamHandle = NULL; SAMPR_HANDLE DomainHandle = NULL; PLSAPR_POLICY_INFORMATION PolicyInfo = NULL;
//
// Open SAM to get the account information
//
Status = SamIConnect( NULL, // no server name
&SamHandle, 0, // no desired access
TRUE // trusted caller
);
if (!NT_SUCCESS(Status)) { goto Cleanup; }
if(InterlockedCompareExchangePointer( &KerbGlobalSamHandle, SamHandle, NULL ) != NULL) { SamrCloseHandle( &SamHandle ); }
Status = LsaIQueryInformationPolicyTrusted( PolicyAccountDomainInformation, &PolicyInfo );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
Status = SamrOpenDomain( KerbGlobalSamHandle, 0, // no desired access
(PRPC_SID) PolicyInfo->PolicyAccountDomainInfo.DomainSid, &DomainHandle );
LsaIFree_LSAPR_POLICY_INFORMATION( PolicyAccountDomainInformation, PolicyInfo );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
if(InterlockedCompareExchangePointer( &KerbGlobalDomainHandle, DomainHandle, NULL ) != NULL) { SamrCloseHandle( &DomainHandle ); } }
//
// try by DN first, then by UPN/SAM accountname.
//
Status = SamIGetUserLogonInformationEx( KerbGlobalDomainHandle, SAM_OPEN_BY_DN | SAM_NO_MEMBERSHIPS, UserName, USER_ALL_OWFPASSWORD | // OWFs
USER_ALL_NTPASSWORDPRESENT | // OWF present bits.
USER_ALL_LMPASSWORDPRESENT | // OWF present bits.
USER_ALL_BADPASSWORDCOUNT | // bad password count.
USER_ALL_USERACCOUNTCONTROL, // UserAccountControl - account disabled/etc.
&UserAllInfo, &GroupMembership, &UserHandle );
if (Status == STATUS_OBJECT_NAME_NOT_FOUND || Status == STATUS_OBJECT_NAME_INVALID) { Status = SamIGetUserLogonInformationEx( KerbGlobalDomainHandle, SAM_OPEN_BY_UPN_OR_ACCOUNTNAME | SAM_NO_MEMBERSHIPS, UserName, USER_ALL_OWFPASSWORD | // OWFs
USER_ALL_NTPASSWORDPRESENT | // OWF present bits.
USER_ALL_LMPASSWORDPRESENT | // OWF present bits.
USER_ALL_BADPASSWORDCOUNT | // bad password count.
USER_ALL_USERACCOUNTCONTROL, // UserAccountControl - account disabled/etc.
&UserAllInfo, &GroupMembership, &UserHandle ); }
if ( !NT_SUCCESS(Status) ) { goto Cleanup; }
UserAll = &UserAllInfo->All;
Status = RtlCalculateNtOwfPassword( Password, &NtOwfPassword );
if( !NT_SUCCESS(Status) ) { goto Cleanup; }
Status = STATUS_LOGON_FAILURE;
if (UserAll->UserAccountControl & USER_ACCOUNT_DISABLED) { goto Cleanup; }
if (UserAll->UserAccountControl & USER_ACCOUNT_AUTO_LOCKED) { goto Cleanup; }
if ( !UserAll->NtPasswordPresent ) { if( UserAll->LmPasswordPresent ) { goto Cleanup; }
if (RtlCompareMemory( &NtOwfPassword, &KerbGlobalNullNtOwfPassword, NT_OWF_PASSWORD_LENGTH ) != NT_OWF_PASSWORD_LENGTH) { UpdateLogonStats = TRUE; goto Cleanup; }
} else {
if (RtlCompareMemory( &NtOwfPassword, UserAll->NtOwfPassword.Buffer, NT_OWF_PASSWORD_LENGTH ) != NT_OWF_PASSWORD_LENGTH) { UpdateLogonStats = TRUE; goto Cleanup; } }
//
// success!
//
if ( UserAll->BadPasswordCount ) { //
// successful logon, insure logon status gets updated for the lockout/bad password case.
//
UpdateLogonStats = TRUE; }
Status = STATUS_SUCCESS;
Cleanup:
ZeroMemory( &NtOwfPassword, sizeof(NtOwfPassword) );
if( UserAllInfo != NULL ) { //
// SamIFree zeroes the sensitive fields.
//
SamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo, UserAllInformation ); }
if( UpdateLogonStats ) { SAM_LOGON_STATISTICS LogonStats;
RtlZeroMemory(&LogonStats, sizeof(LogonStats));
if( NT_SUCCESS(Status) ) { LogonStats.StatisticsToApply = USER_LOGON_INTER_SUCCESS_LOGON; } else { LogonStats.StatisticsToApply = USER_LOGON_BAD_PASSWORD; }
SamIUpdateLogonStatistics( UserHandle, &LogonStats ); }
if (UserHandle != NULL) { SamrCloseHandle( &UserHandle ); }
if (GroupMembership.SidAndAttributes != NULL) { SamIFreeSidAndAttributesList(&GroupMembership); }
return Status; }
NTSTATUS NTAPI KerbVerifyCredentials( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { PKERB_VERIFY_CREDENTIALS_REQUEST VerifyRequest = NULL; NTSTATUS Status = STATUS_LOGON_FAILURE;
//
// Verify the request.
//
D_DebugLog((DEB_TRACE, "KerbVerifyCredentials\n"));
//
// only support in proc use of this interface.
//
if( ClientRequest != NULL ) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
if (SubmitBufferSize < sizeof(KERB_VERIFY_CREDENTIALS_REQUEST)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; }
VerifyRequest = (PKERB_VERIFY_CREDENTIALS_REQUEST) ProtocolSubmitBuffer;
#if 0 // only needed if out-proc supported.
//
// Normalize the strings
//
if( ClientRequest != NULL ) { NULL_RELOCATE_ONE(&VerifyRequest->UserName); NULL_RELOCATE_ONE(&VerifyRequest->DomainName); NULL_RELOCATE_ONE(&VerifyRequest->Password); } #endif
*ProtocolReturnBuffer = NULL; *ReturnBufferLength = 0;
Status = VerifyCredentials( &VerifyRequest->UserName, &VerifyRequest->DomainName, &VerifyRequest->Password );
Cleanup:
*ProtocolStatus = Status; return(STATUS_SUCCESS); }
|