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.
 
 
 
 
 
 

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