Leaked source code of windows server 2003
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.
 
 
 
 
 
 

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