mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4764 lines
120 KiB
4764 lines
120 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1992 - 1996
|
|
//
|
|
// File: miscapi.cxx
|
|
//
|
|
// Contents: Code for miscellaneous lsa mode Kerberos entrypoints
|
|
//
|
|
//
|
|
// History: 16-April-1996 MikeSw Created
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
#include <kerb.hxx>
|
|
#include <kerbp.h>
|
|
#include <crypt.h> // NT_OWF_PASSWORD_LENGTH
|
|
#include <kerbpass.h>
|
|
|
|
#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
|
|
);
|
|
|
|
PLSA_AP_CALL_PACKAGE
|
|
KerbCallPackageDispatch[] = {
|
|
#if DBG
|
|
KerbDebugRequest,
|
|
#else
|
|
NULL,
|
|
#endif
|
|
KerbQueryTicketCache,
|
|
KerbChangeMachinePassword,
|
|
KerbVerifyPac,
|
|
KerbRetrieveTicket,
|
|
KerbSetIpAddresses,
|
|
KerbPurgeTicket,
|
|
KerbChangePassword,
|
|
KerbRetrieveEncodedTicket,
|
|
#if DBG
|
|
KerbDecryptMessage,
|
|
#else
|
|
NULL,
|
|
#endif
|
|
KerbAddBindingCacheEntry,
|
|
KerbSetPassword,
|
|
KerbSetPassword,
|
|
KerbVerifyCredentials,
|
|
KerbQueryTicketCacheEx,
|
|
KerbPurgeTicketEx,
|
|
// KerbRetrieveEncodedTicketEx,
|
|
};
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// 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;
|
|
|
|
case KERB_DEBUG_CREATE_TOKEN:
|
|
{
|
|
UNICODE_STRING String, String2;
|
|
ULONG AuthDataSize = 0;
|
|
HANDLE TokenHandle = NULL;
|
|
LUID LogonId;
|
|
NTSTATUS SubStatus;
|
|
|
|
RtlInitUnicodeString(
|
|
&String,
|
|
L"Administrator"
|
|
);
|
|
RtlInitUnicodeString(
|
|
&String2,
|
|
NULL
|
|
);
|
|
|
|
Status = LsaFunctions->OpenSamUser(
|
|
&String,
|
|
SecNameSamCompatible,
|
|
&String2,
|
|
TRUE, // allow guest
|
|
0, // reserved
|
|
&Handle
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR,"Failed to open sam user: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = LsaFunctions->GetUserAuthData(
|
|
Handle,
|
|
&AuthData,
|
|
&AuthDataSize
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR,"Failed to get auth data: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Now create that token
|
|
//
|
|
|
|
Status = LsaFunctions->ConvertAuthDataToToken(
|
|
AuthData,
|
|
AuthDataSize,
|
|
SecurityImpersonation,
|
|
&KerberosSource,
|
|
Network,
|
|
&String,
|
|
&TokenHandle,
|
|
&LogonId,
|
|
&AccountName,
|
|
&SubStatus
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR,"Failed to create token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
|
|
goto Cleanup;
|
|
}
|
|
NtClose(TokenHandle);
|
|
DebugLog((DEB_ERROR,"Logged on account is %wZ. %ws, line %d\n",&AccountName, THIS_FILE, __LINE__));
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if( Handle != NULL )
|
|
{
|
|
LsaFunctions->CloseSamUser( Handle );
|
|
}
|
|
|
|
if( AuthData != NULL )
|
|
{
|
|
LsaFunctions->FreeLsaHeap( AuthData );
|
|
}
|
|
|
|
if( AccountName.Buffer != NULL )
|
|
{
|
|
LsaFunctions->FreeLsaHeap( AccountName.Buffer );
|
|
}
|
|
|
|
#else
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
#endif
|
|
return(Status);
|
|
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: 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 += 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 ?
|
|
sizeof( KERB_TICKET_CACHE_INFO_WOW64 ) :
|
|
sizeof( KERB_TICKET_CACHE_INFO );
|
|
#else
|
|
ULONG CacheEntrySize = sizeof( KERB_TICKET_CACHE_INFO );
|
|
DsysAssert( WowClient == FALSE );
|
|
#endif // _WIN64
|
|
|
|
KERB_TICKET_CACHE * TicketCaches[2] = {
|
|
&PrimaryCredentials->AuthenticationTicketCache,
|
|
&PrimaryCredentials->ServerTicketCache
|
|
};
|
|
|
|
if ( *CacheSize == 0 ) {
|
|
|
|
*CacheSize = FIELD_OFFSET( KERB_QUERY_TKT_CACHE_RESPONSE, Tickets );
|
|
}
|
|
|
|
for ( ULONG i = 0 ; i < 2 ; 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 ?
|
|
sizeof( KERB_TICKET_CACHE_INFO_WOW64 ) :
|
|
sizeof( KERB_TICKET_CACHE_INFO );
|
|
#else
|
|
ULONG CacheEntrySize = sizeof( KERB_TICKET_CACHE_INFO );
|
|
DsysAssert( WowClient == FALSE );
|
|
#endif // _WIN64
|
|
|
|
KERB_TICKET_CACHE * TicketCaches[2] = {
|
|
&PrimaryCredentials->AuthenticationTicketCache,
|
|
&PrimaryCredentials->ServerTicketCache
|
|
};
|
|
|
|
if ( *Where == NULL ) {
|
|
|
|
*Where = ( PBYTE )( CacheResponse->Tickets ) + CacheResponse->CountOfTickets * CacheEntrySize;
|
|
}
|
|
|
|
for ( ULONG i = 0 ; i < 2 ; 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
|
|
//
|
|
|
|
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.
|
|
//
|
|
|
|
CacheResponse = ( PKERB_QUERY_TKT_CACHE_RESPONSE )KerbAllocate( 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 );
|
|
}
|
|
|
|
KerbFree( 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 ?
|
|
sizeof( KERB_TICKET_CACHE_INFO_EX_WOW64 ) :
|
|
sizeof( KERB_TICKET_CACHE_INFO_EX );
|
|
#else
|
|
ULONG CacheEntrySize = sizeof( KERB_TICKET_CACHE_INFO_EX );
|
|
DsysAssert( WowClient == FALSE );
|
|
#endif // _WIN64
|
|
|
|
KERB_TICKET_CACHE * TicketCaches[2] = {
|
|
&PrimaryCredentials->AuthenticationTicketCache,
|
|
&PrimaryCredentials->ServerTicketCache
|
|
};
|
|
|
|
if ( *CacheSize == 0 ) {
|
|
|
|
*CacheSize = FIELD_OFFSET( KERB_QUERY_TKT_CACHE_EX_RESPONSE, Tickets );
|
|
}
|
|
|
|
for ( ULONG i = 0 ; i < 2 ; 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 ?
|
|
sizeof( KERB_TICKET_CACHE_INFO_EX_WOW64 ) :
|
|
sizeof( KERB_TICKET_CACHE_INFO_EX );
|
|
#else
|
|
ULONG CacheEntrySize = sizeof( KERB_TICKET_CACHE_INFO_EX );
|
|
DsysAssert( WowClient == FALSE );
|
|
#endif // _WIN64
|
|
|
|
KERB_TICKET_CACHE * TicketCaches[2] = {
|
|
&PrimaryCredentials->AuthenticationTicketCache,
|
|
&PrimaryCredentials->ServerTicketCache
|
|
};
|
|
|
|
if ( *Where == NULL ) {
|
|
|
|
*Where = ( PBYTE )( CacheResponse->Tickets ) + CacheResponse->CountOfTickets * CacheEntrySize;
|
|
}
|
|
|
|
for ( ULONG i = 0 ; i < 2 ; 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: 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
|
|
//
|
|
|
|
KerbReadLockLogonSessions( LogonSession );
|
|
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
|
|
);
|
|
|
|
KerbLockList( &LogonSession->CredmanCredentials );
|
|
CredmanLocked = TRUE;
|
|
|
|
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.
|
|
//
|
|
|
|
CacheResponse = ( PKERB_QUERY_TKT_CACHE_EX_RESPONSE )KerbAllocate( 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 );
|
|
}
|
|
|
|
KerbFree( 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,
|
|
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;
|
|
|
|
*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
|
|
|
|
|
|
//
|
|
// 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->AltTargetDomainName.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
|
|
//
|
|
|
|
TicketResponse = (PKERB_EXTERNAL_TICKET) KerbAllocate(TicketSize);
|
|
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 = CacheEntry->KeyExpirationTime;
|
|
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->AltTargetDomainName,
|
|
&TicketResponse->AltTargetDomainName,
|
|
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 (TicketResponse != NULL)
|
|
{
|
|
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,
|
|
&TicketSize,
|
|
&ClientTicketResponse
|
|
);
|
|
|
|
KerbUnlockTicketCache();
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
*ProtocolReturnBuffer = ClientTicketResponse;
|
|
ClientTicketResponse = NULL;
|
|
*ReturnBufferLength = TicketSize;
|
|
|
|
Cleanup:
|
|
if (LogonSession != NULL)
|
|
{
|
|
KerbDereferenceLogonSession(LogonSession);
|
|
}
|
|
if (CacheEntry != NULL)
|
|
{
|
|
KerbDereferenceTicketCacheEntry(CacheEntry);
|
|
}
|
|
if (ClientTicketResponse != NULL)
|
|
{
|
|
LsaFunctions->FreeClientBuffer(
|
|
NULL,
|
|
ClientTicketResponse
|
|
);
|
|
}
|
|
|
|
*ProtocolStatus = Status;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbSetIpAddresses
|
|
//
|
|
// Synopsis: Saves the IP addresses passed in by netlogon
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Same as Callpackage
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
KerbSetIpAddresses(
|
|
IN PLSA_CLIENT_REQUEST ClientRequest,
|
|
IN PVOID ProtocolSubmitBuffer,
|
|
IN PVOID ClientBufferBase,
|
|
IN ULONG SubmitBufferLength,
|
|
OUT PVOID *ProtocolReturnBuffer,
|
|
OUT PULONG ReturnBufferLength,
|
|
OUT PNTSTATUS ProtocolStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PKERB_UPDATE_ADDRESSES_REQUEST UpdateRequest;
|
|
|
|
//
|
|
// This can only be called internally.
|
|
//
|
|
|
|
if (ClientRequest != NULL)
|
|
{
|
|
DebugLog((DEB_ERROR,"Can't update addresses from outside process. %ws, line %d\n", THIS_FILE, __LINE__));
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Verify the request.
|
|
//
|
|
|
|
if (SubmitBufferLength < FIELD_OFFSET(KERB_UPDATE_ADDRESSES_REQUEST, Addresses))
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
UpdateRequest = (PKERB_UPDATE_ADDRESSES_REQUEST) ProtocolSubmitBuffer;
|
|
|
|
//
|
|
// Validate the input
|
|
//
|
|
|
|
|
|
if (SubmitBufferLength < (sizeof(KERB_UPDATE_ADDRESSES_REQUEST)
|
|
+ UpdateRequest->AddressCount * (sizeof(SOCKET_ADDRESS) + sizeof(struct sockaddr_in))
|
|
- ANYSIZE_ARRAY * sizeof(ULONG)))
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status= KerbUpdateGlobalAddresses(
|
|
(PSOCKET_ADDRESS) UpdateRequest->Addresses,
|
|
UpdateRequest->AddressCount
|
|
);
|
|
|
|
|
|
//
|
|
// Copy them into the global for others to use
|
|
//
|
|
|
|
|
|
Cleanup:
|
|
|
|
*ProtocolStatus = Status;
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbVerifyPac
|
|
//
|
|
// Synopsis: Verifies that a PAC was signed by a valid KDC
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Same as for LsaApCallAuthenticationPackage. The submit
|
|
// buffer must contain a KERB_VERIFY_PAC_REQUEST message.
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: STATUS_SUCCESS. The real error is in the protocol status.
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS NTAPI
|
|
KerbVerifyPac(
|
|
IN PLSA_CLIENT_REQUEST ClientRequest,
|
|
IN PVOID ProtocolSubmitBuffer,
|
|
IN PVOID ClientBufferBase,
|
|
IN ULONG SubmitBufferLength,
|
|
OUT PVOID *ProtocolReturnBuffer,
|
|
OUT PULONG ReturnBufferLength,
|
|
OUT PNTSTATUS ProtocolStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PKERB_VERIFY_PAC_REQUEST VerifyRequest;
|
|
DWORD MaxBufferSize;
|
|
|
|
if (ARGUMENT_PRESENT(ProtocolReturnBuffer))
|
|
{
|
|
*ProtocolReturnBuffer = NULL;
|
|
}
|
|
if (ARGUMENT_PRESENT(ReturnBufferLength))
|
|
{
|
|
*ReturnBufferLength = 0;
|
|
}
|
|
if (SubmitBufferLength < sizeof(*VerifyRequest)) {
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!KerbGlobalInitialized)
|
|
{
|
|
Status = STATUS_SUCCESS;
|
|
DsysAssert(FALSE);
|
|
goto Cleanup;
|
|
}
|
|
|
|
VerifyRequest = (PKERB_VERIFY_PAC_REQUEST) ProtocolSubmitBuffer;
|
|
|
|
MaxBufferSize = SubmitBufferLength -
|
|
FIELD_OFFSET(KERB_VERIFY_PAC_REQUEST, ChecksumAndSignature);
|
|
if ((VerifyRequest->ChecksumLength > MaxBufferSize) ||
|
|
(VerifyRequest->SignatureLength > MaxBufferSize) ||
|
|
((VerifyRequest->ChecksumLength + VerifyRequest->SignatureLength) >
|
|
MaxBufferSize))
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (KerbKdcHandle == NULL)
|
|
{
|
|
Status = STATUS_MUST_BE_KDC;
|
|
goto Cleanup;
|
|
}
|
|
|
|
DsysAssert(KerbKdcVerifyPac != NULL);
|
|
|
|
Status = (*KerbKdcVerifyPac)(
|
|
VerifyRequest->ChecksumLength,
|
|
VerifyRequest->ChecksumAndSignature,
|
|
VerifyRequest->SignatureType,
|
|
VerifyRequest->SignatureLength,
|
|
VerifyRequest->ChecksumAndSignature + VerifyRequest->ChecksumLength
|
|
);
|
|
Cleanup:
|
|
*ProtocolStatus = Status;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
KerbPurgePrimaryCredentialsTickets(
|
|
IN KERB_PRIMARY_CREDENTIAL * PrimaryCredentials,
|
|
IN OPTIONAL PUNICODE_STRING ServerName,
|
|
IN OPTIONAL PUNICODE_STRING ServerRealm
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
DsysAssert( PrimaryCredentials );
|
|
|
|
if ( ServerName == NULL && ServerRealm == NULL ) {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
KerbPurgeTicketCache( &PrimaryCredentials->AuthenticationTicketCache );
|
|
KerbPurgeTicketCache( &PrimaryCredentials->ServerTicketCache );
|
|
|
|
} else if ( ServerName != NULL && ServerRealm != NULL ) {
|
|
|
|
KERB_TICKET_CACHE * TicketCaches[2] = {
|
|
&PrimaryCredentials->AuthenticationTicketCache,
|
|
&PrimaryCredentials->ServerTicketCache
|
|
};
|
|
|
|
//
|
|
// Prowl through the caches and remove all the matching tickets
|
|
//
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
|
|
KerbWriteLockTicketCache();
|
|
|
|
for ( ULONG i = 0 ; i < 2 ; 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"));
|
|
|
|
KerbCleanupSpnCache();
|
|
|
|
#if _WIN64
|
|
|
|
SECPKG_CALL_INFO CallInfo;
|
|
|
|
if(!LsaFunctions->GetCallInfo(&CallInfo))
|
|
{
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT)
|
|
{
|
|
StructureSize = sizeof( KERB_PURGE_TKT_CACHE_REQUEST_WOW64 );
|
|
}
|
|
|
|
#endif // _WIN64
|
|
|
|
if (SubmitBufferSize < StructureSize)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
#if _WIN64
|
|
|
|
KERB_PURGE_TKT_CACHE_REQUEST LocalPurgeRequest;
|
|
|
|
if (CallInfo.Attributes & SECPKG_CALL_WOWCLIENT)
|
|
{
|
|
//
|
|
// Thunk 32-bit pointers if this is a WOW caller
|
|
//
|
|
|
|
PKERB_PURGE_TKT_CACHE_REQUEST_WOW64 PurgeRequestWOW =
|
|
( PKERB_PURGE_TKT_CACHE_REQUEST_WOW64 )PurgeRequest;
|
|
|
|
LocalPurgeRequest.MessageType = PurgeRequestWOW->MessageType;
|
|
LocalPurgeRequest.LogonId = PurgeRequestWOW->LogonId;
|
|
|
|
UNICODE_STRING_FROM_WOW_STRING(
|
|
&LocalPurgeRequest.ServerName,
|
|
&PurgeRequestWOW->ServerName );
|
|
|
|
UNICODE_STRING_FROM_WOW_STRING(
|
|
&LocalPurgeRequest.RealmName,
|
|
&PurgeRequestWOW->RealmName );
|
|
|
|
PurgeRequest = &LocalPurgeRequest;
|
|
}
|
|
|
|
#endif // _WIN64
|
|
|
|
//
|
|
// Normalize the strings
|
|
//
|
|
|
|
NULL_RELOCATE_ONE( &PurgeRequest->ServerName );
|
|
NULL_RELOCATE_ONE( &PurgeRequest->RealmName );
|
|
|
|
//
|
|
// Find the callers logon id & TCB status
|
|
//
|
|
|
|
Status = LsaFunctions->GetClientInfo( &ClientInfo );
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If the caller did not provide a logon id, use the caller's logon id.
|
|
//
|
|
|
|
if ( RtlIsZeroLuid( &PurgeRequest->LogonId )) {
|
|
|
|
LogonId = &ClientInfo.LogonId;
|
|
|
|
} else if ( !ClientInfo.HasTcbPrivilege ) {
|
|
|
|
//
|
|
// The caller must have TCB privilege in order to access someone
|
|
// else's ticket cache.
|
|
//
|
|
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
goto Cleanup;
|
|
|
|
} else {
|
|
|
|
LogonId = &PurgeRequest->LogonId;
|
|
}
|
|
|
|
LogonSession = KerbReferenceLogonSession(
|
|
LogonId,
|
|
FALSE // don't unlink
|
|
);
|
|
|
|
if (LogonSession == NULL)
|
|
{
|
|
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If no servername / realm name were supplied, purge all tickets
|
|
//
|
|
|
|
if ((PurgeRequest->ServerName.Length) == 0 && (PurgeRequest->RealmName.Length == 0))
|
|
{
|
|
D_DebugLog((DEB_TRACE, "Purging all tickets\n"));
|
|
|
|
Status = KerbPurgePrimaryCredentialsTickets(
|
|
&LogonSession->PrimaryCredentials,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
} else {
|
|
|
|
D_DebugLog(( DEB_TRACE, "Purging tickets %wZ\\%wZ\n",
|
|
&PurgeRequest->RealmName,
|
|
&PurgeRequest->ServerName ));
|
|
|
|
Status = KerbPurgePrimaryCredentialsTickets(
|
|
&LogonSession->PrimaryCredentials,
|
|
&PurgeRequest->ServerName,
|
|
&PurgeRequest->RealmName
|
|
);
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (LogonSession != NULL)
|
|
{
|
|
KerbDereferenceLogonSession(LogonSession);
|
|
}
|
|
|
|
*ProtocolReturnBuffer = NULL;
|
|
*ReturnBufferLength = 0;
|
|
|
|
*ProtocolStatus = Status;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbPurgeTicketEx
|
|
//
|
|
// Synopsis: Removes ticket from the ticket cache
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Same as Callpackage
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS NTAPI
|
|
KerbPurgeTicketEx(
|
|
IN PLSA_CLIENT_REQUEST ClientRequest,
|
|
IN PVOID ProtocolSubmitBuffer,
|
|
IN PVOID ClientBufferBase,
|
|
IN ULONG SubmitBufferSize,
|
|
OUT PVOID *ProtocolReturnBuffer,
|
|
OUT PULONG ReturnBufferLength,
|
|
OUT PNTSTATUS ProtocolStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG StructureSize = sizeof( KERB_PURGE_TKT_CACHE_EX_REQUEST );
|
|
PKERB_PURGE_TKT_CACHE_EX_REQUEST PurgeRequest = ( PKERB_PURGE_TKT_CACHE_EX_REQUEST )ProtocolSubmitBuffer;
|
|
SECPKG_CLIENT_INFO ClientInfo;
|
|
PLUID LogonId;
|
|
PKERB_LOGON_SESSION LogonSession = NULL;
|
|
|
|
//
|
|
// Verify the request.
|
|
//
|
|
|
|
KerbCleanupSpnCache();
|
|
|
|
D_DebugLog((DEB_TRACE, "Puring ticket cache Ex\n"));
|
|
|
|
#if _WIN64
|
|
|
|
SECPKG_CALL_INFO CallInfo;
|
|
|
|
if( !LsaFunctions->GetCallInfo( &CallInfo )) {
|
|
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) {
|
|
|
|
StructureSize = sizeof( KERB_PURGE_TKT_CACHE_EX_REQUEST_WOW64 );
|
|
}
|
|
|
|
#endif
|
|
|
|
if ( SubmitBufferSize < StructureSize ) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
#if _WIN64
|
|
|
|
KERB_PURGE_TKT_CACHE_EX_REQUEST LocalPurgeRequest;
|
|
|
|
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) {
|
|
|
|
//
|
|
// Thunk 32-bit pointers if this is a WOW caller
|
|
//
|
|
|
|
PKERB_PURGE_TKT_CACHE_EX_REQUEST_WOW64 PurgeRequestWOW =
|
|
( PKERB_PURGE_TKT_CACHE_EX_REQUEST_WOW64 )PurgeRequest;
|
|
|
|
LocalPurgeRequest.MessageType = PurgeRequestWOW->MessageType;
|
|
LocalPurgeRequest.LogonId = PurgeRequestWOW->LogonId;
|
|
|
|
UNICODE_STRING_FROM_WOW_STRING(
|
|
&LocalPurgeRequest.TicketTemplate.ClientName,
|
|
&PurgeRequestWOW->TicketTemplate.ClientName );
|
|
|
|
UNICODE_STRING_FROM_WOW_STRING(
|
|
&LocalPurgeRequest.TicketTemplate.ClientRealm,
|
|
&PurgeRequestWOW->TicketTemplate.ClientRealm );
|
|
|
|
UNICODE_STRING_FROM_WOW_STRING(
|
|
&LocalPurgeRequest.TicketTemplate.ServerName,
|
|
&PurgeRequestWOW->TicketTemplate.ServerName );
|
|
|
|
UNICODE_STRING_FROM_WOW_STRING(
|
|
&LocalPurgeRequest.TicketTemplate.ServerRealm,
|
|
&PurgeRequestWOW->TicketTemplate.ServerRealm );
|
|
|
|
LocalPurgeRequest.TicketTemplate.StartTime = PurgeRequestWOW->TicketTemplate.StartTime;
|
|
LocalPurgeRequest.TicketTemplate.EndTime = PurgeRequestWOW->TicketTemplate.EndTime;
|
|
LocalPurgeRequest.TicketTemplate.RenewTime = PurgeRequestWOW->TicketTemplate.RenewTime;
|
|
LocalPurgeRequest.TicketTemplate.EncryptionType = PurgeRequestWOW->TicketTemplate.EncryptionType;
|
|
LocalPurgeRequest.TicketTemplate.TicketFlags = PurgeRequestWOW->TicketTemplate.TicketFlags;
|
|
|
|
PurgeRequest = &LocalPurgeRequest;
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Normalize the strings
|
|
//
|
|
|
|
NULL_RELOCATE_ONE( &PurgeRequest->TicketTemplate.ClientName );
|
|
NULL_RELOCATE_ONE( &PurgeRequest->TicketTemplate.ClientRealm );
|
|
NULL_RELOCATE_ONE( &PurgeRequest->TicketTemplate.ServerName );
|
|
NULL_RELOCATE_ONE( &PurgeRequest->TicketTemplate.ServerRealm );
|
|
|
|
//
|
|
// Find the callers logon id & TCB status
|
|
//
|
|
|
|
Status = LsaFunctions->GetClientInfo( &ClientInfo );
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If the caller did not provide a logon id, use the caller's logon id
|
|
//
|
|
|
|
if ( RtlIsZeroLuid( &PurgeRequest->LogonId )) {
|
|
|
|
LogonId = &ClientInfo.LogonId;
|
|
|
|
} else if ( !ClientInfo.HasTcbPrivilege ) {
|
|
|
|
//
|
|
// The caller is required to have the TCB privilege
|
|
// in order to access someone else's ticket cache
|
|
//
|
|
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
goto Cleanup;
|
|
|
|
} else {
|
|
|
|
LogonId = &PurgeRequest->LogonId;
|
|
}
|
|
|
|
LogonSession = KerbReferenceLogonSession(
|
|
LogonId,
|
|
FALSE
|
|
);
|
|
|
|
if ( LogonSession == NULL ) {
|
|
|
|
Status = STATUS_NO_SUCH_LOGON_SESSION;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Purge the entire ticket cache?
|
|
//
|
|
|
|
if ( PurgeRequest->Flags & KERB_PURGE_ALL_TICKETS ) {
|
|
|
|
D_DebugLog(( DEB_TRACE, "Purging all tickets\n" ));
|
|
|
|
Status = KerbPurgePrimaryCredentialsTickets(
|
|
&LogonSession->PrimaryCredentials,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
DsysAssert( NT_SUCCESS( Status ));
|
|
|
|
KerbLockList( &LogonSession->CredmanCredentials );
|
|
|
|
for ( PLIST_ENTRY ListEntry = LogonSession->CredmanCredentials.List.Flink;
|
|
ListEntry != &LogonSession->CredmanCredentials.List;
|
|
ListEntry = ListEntry->Flink ) {
|
|
|
|
PKERB_CREDMAN_CRED CredmanCred = CONTAINING_RECORD(
|
|
ListEntry,
|
|
KERB_CREDMAN_CRED,
|
|
ListEntry.Next
|
|
);
|
|
|
|
if ( CredmanCred->SuppliedCredentials == NULL) {
|
|
|
|
continue;
|
|
}
|
|
|
|
Status = KerbPurgePrimaryCredentialsTickets(
|
|
CredmanCred->SuppliedCredentials,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
DsysAssert( NT_SUCCESS( Status ));
|
|
}
|
|
|
|
KerbUnlockList( &LogonSession->CredmanCredentials );
|
|
|
|
} else {
|
|
|
|
BOOLEAN MatchClient = (
|
|
PurgeRequest->TicketTemplate.ClientName.Length > 0 ||
|
|
PurgeRequest->TicketTemplate.ClientRealm.Length > 0 );
|
|
|
|
BOOLEAN MatchServer = (
|
|
PurgeRequest->TicketTemplate.ServerName.Length > 0 ||
|
|
PurgeRequest->TicketTemplate.ServerRealm.Length > 0 );
|
|
|
|
BOOLEAN Found = FALSE;
|
|
|
|
if ( !MatchServer ) {
|
|
|
|
//
|
|
// Nothing will match these constraints
|
|
//
|
|
|
|
Status = STATUS_OBJECT_NAME_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// 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 ))) {
|
|
|
|
Status = KerbPurgePrimaryCredentialsTickets(
|
|
&LogonSession->PrimaryCredentials,
|
|
&PurgeRequest->TicketTemplate.ServerName,
|
|
&PurgeRequest->TicketTemplate.ServerRealm
|
|
);
|
|
|
|
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 ))) {
|
|
|
|
Status = KerbPurgePrimaryCredentialsTickets(
|
|
CredmanCred->SuppliedCredentials,
|
|
&PurgeRequest->TicketTemplate.ServerName,
|
|
&PurgeRequest->TicketTemplate.ServerRealm
|
|
);
|
|
|
|
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,"Failed to get outbound ticket: 0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Encode the ticket or kerb_cred
|
|
//
|
|
|
|
KerbReadLockTicketCache();
|
|
|
|
Status = KerbPackExternalTicket(
|
|
CacheEntry,
|
|
RetrieveRequest->CacheOptions & KERB_RETRIEVE_TICKET_AS_KERB_CRED,
|
|
&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
|
|
//
|
|
|
|
DecryptResponse = (PBYTE) KerbAllocate(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);
|
|
}
|
|
|
|
if (DecryptResponse != NULL)
|
|
{
|
|
KerbFree(DecryptResponse);
|
|
}
|
|
if (ClientResponse != NULL)
|
|
{
|
|
LsaFunctions->FreeClientBuffer(
|
|
NULL,
|
|
ClientResponse
|
|
);
|
|
}
|
|
*ProtocolStatus = Status;
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbAddBindingCacheEntry
|
|
//
|
|
// Synopsis: Adds an entry to the binding cache
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: Same as Callpackage
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS NTAPI
|
|
KerbAddBindingCacheEntry(
|
|
IN PLSA_CLIENT_REQUEST ClientRequest,
|
|
IN PVOID ProtocolSubmitBuffer,
|
|
IN PVOID ClientBufferBase,
|
|
IN ULONG SubmitBufferSize,
|
|
OUT PVOID *ProtocolReturnBuffer,
|
|
OUT PULONG ReturnBufferLength,
|
|
OUT PNTSTATUS ProtocolStatus
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
SECPKG_CLIENT_INFO ClientInfo;
|
|
PKERB_ADD_BINDING_CACHE_ENTRY_REQUEST BindingRequest = ( PKERB_ADD_BINDING_CACHE_ENTRY_REQUEST )ProtocolSubmitBuffer;
|
|
PKERB_BINDING_CACHE_ENTRY CacheEntry = NULL;
|
|
ULONG StructureSize = sizeof( KERB_ADD_BINDING_CACHE_ENTRY_REQUEST );
|
|
|
|
//
|
|
// Verify the request.
|
|
//
|
|
|
|
D_DebugLog(( DEB_TRACE, "Addding binding cache entry\n" ));
|
|
|
|
#if _WIN64
|
|
|
|
SECPKG_CALL_INFO CallInfo;
|
|
|
|
//
|
|
// Return 32-bit cache entries if this is a WOW caller
|
|
//
|
|
|
|
if(!LsaFunctions->GetCallInfo( &CallInfo ))
|
|
{
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) {
|
|
|
|
StructureSize = sizeof( KERB_ADD_BINDING_CACHE_ENTRY_REQUEST_WOW64 );
|
|
}
|
|
|
|
#endif // _WIN64
|
|
|
|
if ( SubmitBufferSize < StructureSize ) {
|
|
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
#if _WIN64
|
|
|
|
KERB_ADD_BINDING_CACHE_ENTRY_REQUEST LocalBindingRequest;
|
|
|
|
if ( CallInfo.Attributes & SECPKG_CALL_WOWCLIENT ) {
|
|
|
|
//
|
|
// Thunk 32-bit pointers if this is a WOW caller
|
|
//
|
|
|
|
PKERB_ADD_BINDING_CACHE_ENTRY_REQUEST_WOW64 BindingRequestWOW =
|
|
( PKERB_ADD_BINDING_CACHE_ENTRY_REQUEST_WOW64 )BindingRequest;
|
|
|
|
LocalBindingRequest.MessageType = BindingRequestWOW->MessageType;
|
|
|
|
UNICODE_STRING_FROM_WOW_STRING(
|
|
&LocalBindingRequest.RealmName,
|
|
&BindingRequestWOW->RealmName );
|
|
|
|
UNICODE_STRING_FROM_WOW_STRING(
|
|
&LocalBindingRequest.KdcAddress,
|
|
&BindingRequestWOW->KdcAddress );
|
|
|
|
LocalBindingRequest.AddressType = BindingRequestWOW->AddressType;
|
|
|
|
BindingRequest = &LocalBindingRequest;
|
|
}
|
|
|
|
#endif // _WIN64
|
|
|
|
//
|
|
// Normalize the strings
|
|
//
|
|
|
|
NULL_RELOCATE_ONE( &BindingRequest->RealmName );
|
|
NULL_RELOCATE_ONE( &BindingRequest->KdcAddress );
|
|
|
|
//
|
|
// Find the callers logon id & TCB status
|
|
//
|
|
|
|
Status = LsaFunctions->GetClientInfo( &ClientInfo );
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Require the caller to have TCB.
|
|
//
|
|
|
|
if ( !ClientInfo.HasTcbPrivilege ) {
|
|
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = KerbCacheBinding(
|
|
&BindingRequest->RealmName,
|
|
&BindingRequest->KdcAddress,
|
|
BindingRequest->AddressType,
|
|
0,
|
|
0,
|
|
0,
|
|
&CacheEntry
|
|
);
|
|
|
|
Cleanup:
|
|
|
|
if ( CacheEntry != NULL ) {
|
|
|
|
KerbDereferenceBindingCacheEntry( CacheEntry );
|
|
}
|
|
|
|
*ProtocolReturnBuffer = NULL;
|
|
*ReturnBufferLength = 0;
|
|
*ProtocolStatus = Status;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
VerifyCredentials(
|
|
IN PUNICODE_STRING UserName,
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING Password
|
|
)
|
|
{
|
|
SAMPR_HANDLE UserHandle = NULL;
|
|
PSAMPR_USER_INFO_BUFFER UserAllInfo = NULL;
|
|
PSAMPR_USER_ALL_INFORMATION UserAll;
|
|
SID_AND_ATTRIBUTES_LIST GroupMembership;
|
|
|
|
NT_OWF_PASSWORD NtOwfPassword;
|
|
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 );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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_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)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
} else {
|
|
|
|
if (RtlCompareMemory(
|
|
&NtOwfPassword,
|
|
UserAll->NtOwfPassword.Buffer,
|
|
NT_OWF_PASSWORD_LENGTH
|
|
) != NT_OWF_PASSWORD_LENGTH)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
ZeroMemory( &NtOwfPassword, sizeof(NtOwfPassword) );
|
|
|
|
if( UserAllInfo != NULL )
|
|
{
|
|
//
|
|
// SamIFree now zeroes the sensitive fields.
|
|
//
|
|
|
|
SamIFree_SAMPR_USER_INFO_BUFFER( UserAllInfo, UserAllInformation );
|
|
}
|
|
|
|
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);
|
|
}
|
|
|