//+----------------------------------------------------------------------- // // 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 #include #include // NT_OWF_PASSWORD_LENGTH #include #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 KerbQuerySupplementalCredentials( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, 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, KerbRefreshSmartcardCredentials, KerbAddExtraCredential, KerbQuerySupplementalCredentials }; //+------------------------------------------------------------------------- // // 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]; 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; #ifdef ALLOW_TOKEN_CREATION 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; } #endif // ALLOW_TOKEN_CREATION 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 SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { NTSTATUS Status = STATUS_SUCCESS; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_REFRESH_SCCRED_REQUEST RefreshRequest; SECPKG_CLIENT_INFO ClientInfo; PLUID LogonId; if (SubmitBufferSize < sizeof(KERB_REFRESH_SCCRED_REQUEST)) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } RefreshRequest = (PKERB_REFRESH_SCCRED_REQUEST) ProtocolSubmitBuffer; 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 ) != 0) { Status = STATUS_NOT_SUPPORTED; goto Cleanup; } #endif // _WIN64 // // 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( &RefreshRequest->LogonId )) { LogonId = &ClientInfo.LogonId; } else if ( (!ClientInfo.HasTcbPrivilege) || ( RefreshRequest->Flags == KERB_REFRESH_SCCRED_GETTGT )) { // // We can't do this w/o TCB. && we can only get TGTs for ourselves // due to pin caching / impersonation issues. // Status = STATUS_PRIVILEGE_NOT_HELD; goto Cleanup; } else { LogonId = &RefreshRequest->LogonId; } NULL_RELOCATE_ONE( &RefreshRequest->CredentialBlob ); LogonSession = KerbReferenceLogonSession( LogonId, FALSE ); DsysAssert(LogonSession != NULL); if (LogonSession == NULL) { Status = STATUS_NO_SUCH_LOGON_SESSION; goto Cleanup; } if ( RefreshRequest->Flags == KERB_REFRESH_SCCRED_RELEASE ) { KerbReleasePkCreds( LogonSession, NULL, TRUE // ok for reuse - save PIN and SChelper data ); } else if ( RefreshRequest->Flags == KERB_REFRESH_SCCRED_GETTGT ) { Status = KerbTicklePackage( LogonSession, &RefreshRequest->CredentialBlob ); if (!NT_SUCCESS( Status )) { goto Cleanup; } } else { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } 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 (( CacheEntry->ServiceName->NameCount == 2 ) && ( !AllowTgtSessionKey ) && ( RtlEqualUnicodeString( &CacheEntry->ServiceName->Names[0], &KerbGlobalKdcServiceName, TRUE ))) { D_DebugLog((DEB_ERROR, "Zeroing out session key\n")); 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); } //+------------------------------------------------------------------------- // // 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, TotalBufferSize; if (ARGUMENT_PRESENT(ProtocolReturnBuffer)) { *ProtocolReturnBuffer = NULL; } if (ARGUMENT_PRESENT(ReturnBufferLength)) { *ReturnBufferLength = 0; } if (SubmitBufferLength < sizeof(KERB_VERIFY_PAC_REQUEST)) { 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); TotalBufferSize = VerifyRequest->ChecksumLength + VerifyRequest->SignatureLength; if (( VerifyRequest->ChecksumLength > MaxBufferSize ) || ( VerifyRequest->SignatureLength > MaxBufferSize ) || ( TotalBufferSize > MaxBufferSize )) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Make sure we actually have valid data in the rest of the submit buffer. // if (SubmitBufferLength < TotalBufferSize + FIELD_OFFSET(KERB_VERIFY_PAC_REQUEST, ChecksumAndSignature)) { DsysAssert(FALSE); 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 // #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")); KerbCleanupSpnCache(); 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 // #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" )); KerbCleanupSpnCache(); 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; PKERB_SPN_CACHE_ENTRY SpnCacheEntry = 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, &SpnCacheEntry ); 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, SpnCacheEntry, 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 ); } if ( SpnCacheEntry ) { KerbDereferenceSpnCacheEntry( SpnCacheEntry ); } 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, "Adding 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: RtlSecureZeroMemory( &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); } //+------------------------------------------------------------------------- // // Function: KerbQuerySupplementalCredentials // // Synopsis: Takes a marshalled credman blob, and gets // a TGT and pwd information. // // Effects: // // Arguments: Same as Callpackage // // Requires: In process call only! // // Returns: // // Notes: Free returned buffer using LsapFreeLsa'. // // //-------------------------------------------------------------------------- NTSTATUS NTAPI KerbQuerySupplementalCredentials( IN PLSA_CLIENT_REQUEST ClientRequest, IN PVOID ProtocolSubmitBuffer, IN PVOID ClientBufferBase, IN ULONG SubmitBufferSize, OUT PVOID *ProtocolReturnBuffer, OUT PULONG ReturnBufferLength, OUT PNTSTATUS ProtocolStatus ) { PKERB_QUERY_SUPPLEMENTAL_CREDS_REQUEST Request = (PKERB_QUERY_SUPPLEMENTAL_CREDS_REQUEST) ProtocolSubmitBuffer; PKERB_QUERY_SUPPLEMENTAL_CREDS_RESPONSE Response = NULL; NTSTATUS Status; HANDLE ClientThreadToken = NULL; HANDLE OldToken = NULL; PKERB_LOGON_SESSION LogonSession = NULL; PKERB_CREDMAN_CRED CredmanCredential = NULL; PKERB_PRIMARY_CREDENTIAL PrimaryCredentials = NULL; BOOLEAN Impersonating = FALSE; UNICODE_STRING TargetName; UNICODE_STRING tmp; ULONG ResponseSize = 0; ULONG AdditionalCredFlags = 0; *ProtocolReturnBuffer = NULL; *ReturnBufferLength = 0; D_DebugLog((DEB_TRACE_API, "KerbQuerySupplementalCredentials\n")); // // only support in proc use of this interface. // if (( ClientRequest != NULL ) || ( SubmitBufferSize < sizeof(KERB_QUERY_SUPPLEMENTAL_CREDS_REQUEST) ) || ( Request->MessageType != KerbQuerySupplementalCredentialsMessage )) { Status = STATUS_INVALID_PARAMETER; goto Cleanup; } // // Must be a PKI cred, for now :). // if ( Request->MarshalledCreds->Type != CRED_TYPE_DOMAIN_CERTIFICATE) { D_DebugLog((DEB_ERROR, "Credential is not a pki credential\n")); Status = STATUS_INVALID_PARAMETER; DsysAssert(FALSE); goto Cleanup; } // // We may not know about the logon session. If not, // create a duplicate in the kerberos list. // LogonSession = KerbReferenceLogonSession( &Request->LogonId, FALSE ); if ( LogonSession == NULL ) { SECPKG_CLIENT_INFO ClientInfo; BOOLEAN ClientImpersonating; SECURITY_IMPERSONATION_LEVEL ImpersonationLevel; HANDLE hProcess = NULL; DebugLog((DEB_ERROR, "Missing logon session \n")); DsysAssert(FALSE); Status = LsaFunctions->GetClientInfo(&ClientInfo); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Failed to get client information: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__)); goto Cleanup; } else if ((ClientInfo.ClientFlags & SECPKG_CLIENT_THREAD_TERMINATED) != 0) { Status = STATUS_ACCESS_DENIED; goto Cleanup; } ImpersonationLevel = ClientInfo.ImpersonationLevel; ClientImpersonating = ClientInfo.Impersonating; hProcess = (HANDLE) LongToHandle(ClientInfo.ProcessID); Status = KerbCreateDummyLogonSession( &Request->LogonId, &LogonSession, ImpersonationLevel, ClientImpersonating, hProcess ); if (!NT_SUCCESS( Status) ) { DebugLog((DEB_ERROR, "Failed to create dummy logon session %x\n", Status )); DsysAssert(FALSE); goto Cleanup; } } // // These credman operations require that you impersonate. Do that, but first // save off the old thread token. // Status = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &OldToken ); if (!NT_SUCCESS( Status ) && Status != STATUS_NO_TOKEN ) { DebugLog((DEB_ERROR, "NtOpenThreadToken failed %x\n", Status)); goto Cleanup; } Status = LsaFunctions->OpenTokenByLogonId( &LogonSession->LogonId, &ClientThreadToken ); if (!NT_SUCCESS(Status)) { D_DebugLog((DEB_ERROR,"Unable to get the client token handle.\n")); goto Cleanup; } if(!SetThreadToken(NULL, ClientThreadToken)) { D_DebugLog((DEB_ERROR,"Unable to impersonate the client token handle.\n")); Status = STATUS_CANNOT_IMPERSONATE; goto Cleanup; } Impersonating = TRUE; // // Build a credman cred // RtlInitUnicodeString( &TargetName, Request->MarshalledCreds->TargetName ); Status = KerbConvertCertCredential( LogonSession, Request->MarshalledCreds->UserName, &TargetName, &PrimaryCredentials ); if (!NT_SUCCESS( Status )) { D_DebugLog((DEB_ERROR, "KerbConvertCertCredential failed %x\n")); goto Cleanup; } RtlInitUnicodeString( &tmp, CRED_SESSION_WILDCARD_NAME_W ); if (RtlEqualUnicodeString(&tmp,&TargetName,TRUE)) { AdditionalCredFlags |= RAS_CREDENTIAL; } KerbWriteLockLogonSessions(LogonSession); // // Add it to the logon session - this has a byproduct of asking for a TGT. // Status = KerbAddCredmanCredToLogonSession( LogonSession, PrimaryCredentials, // note: freed by this fn AdditionalCredFlags, &CredmanCredential ); KerbUnlockLogonSessions(LogonSession); if (!NT_SUCCESS( Status )) { DebugLog((DEB_ERROR, "KerbAddCredmanCredToLogonSession failed %x\n", Status)); goto Cleanup; } // // Use the TGT in a U2U fashion - this has a by-product of returning the // supplemental credentials in a key we can decrypt with. // Status = KerbRetrieveOWF( LogonSession, NULL, // no supplied credentials... CredmanCredential, &TargetName, &Response, &ResponseSize ); if (!NT_SUCCESS( Status )) { DebugLog((DEB_ERROR, "KerbRetrieveOWF failed %x\n", Status)); goto Cleanup; } *ProtocolReturnBuffer = Response; *ReturnBufferLength = ResponseSize; Response = NULL; Cleanup: if ( Impersonating ) { if ( OldToken ) { SetThreadToken( NULL, OldToken ); } else { RevertToSelf(); } } if ( OldToken ) { CloseHandle(OldToken); } if ( ClientThreadToken ) { CloseHandle( ClientThreadToken ); } if ( LogonSession ) { KerbDereferenceLogonSession( LogonSession ); } if ( Response ) { LsaFunctions->FreeLsaHeap( Response ); } *ProtocolStatus = Status; return (STATUS_SUCCESS); }