windows-nt/Source/XPSP1/NT/ds/security/protocols/kerberos/client2/ctxtapi.cxx

4472 lines
136 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1996
//
// File: ctxtapi.cxx
//
// Contents: Context APIs for Kerberos package
//
//
// History: 16-April-1996 Created MikeSw
// 26-Sep-1998 ChandanS
// Added more debugging support etc.
//
//------------------------------------------------------------------------
#include <kerb.hxx>
#include <kerbp.h>
#include "kerbevt.h"
#include <gssapip.h>
#include <krbaudit.h>
#ifdef RETAIL_LOG_SUPPORT
static TCHAR THIS_FILE[]=TEXT(__FILE__);
#endif
UNICODE_STRING KerbTargetPrefix = {sizeof(L"krb5://")-sizeof(WCHAR),sizeof(L"krb5://"),L"krb5://" };
#define FILENO FILENO_CTXTAPI
//+-------------------------------------------------------------------------
//
// Function: SpDeleteContext
//
// Synopsis: Deletes a Kerberos context
//
// Effects:
//
// Arguments: ContextHandle - The context to delete
//
// Requires:
//
// Returns: STATUS_SUCCESS or STATUS_INVALID_HANDLE
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpDeleteContext(
IN LSA_SEC_HANDLE ContextHandle
)
{
NTSTATUS Status;
PKERB_CONTEXT Context;
KERB_CONTEXT_STATE ContextState;
ULONG ClientProcess = 0;
D_DebugLog((DEB_TRACE_API,"SpDeleteContext 0x%x called\n",ContextHandle));
if (!KerbGlobalInitialized)
{
Status = STATUS_INVALID_SERVER_STATE;
Context = NULL;
goto Cleanup;
}
Status = KerbReferenceContext(
ContextHandle,
TRUE, // unlink handle
&Context
);
if (Context == NULL)
{
D_DebugLog((DEB_ERROR,"SpDeleteContext: Context 0x%x not located. %ws, line %d\n",ContextHandle, THIS_FILE, __LINE__));
goto Cleanup;
}
#ifndef WIN32_CHICAGO
KerbReadLockContexts();
// Need to copy out the context data else we'll end up holding the lock
// for too long a time.
//
ContextState = Context->ContextState;
ClientProcess = Context->ClientProcess;
KerbUnlockContexts();
// If this was a context that was dropped in the middle,
// audit it as a logon failure.
//
if (ContextState == ApReplySentState)
{
LsaFunctions->AuditLogon(
STATUS_UNFINISHED_CONTEXT_DELETED,
STATUS_SUCCESS,
&Context->ClientName,
&Context->ClientRealm,
NULL, // no workstation
Context->UserSid,
Network,
&KerberosSource,
&Context->LogonId
);
}
#endif // WIN32_CHICAGO
//
// Now dereference the Context. If nobody else is using this Context
// currently it will be freed.
//
KerbDereferenceContext(Context);
Status = STATUS_SUCCESS;
Cleanup:
D_DebugLog((DEB_TRACE_LEAKS,"SpDeleteContext returned 0x%x, Context 0x%x, Pid 0x%x\n",KerbMapKerbNtStatusToNtStatus(Status), Context, ClientProcess));
D_DebugLog((DEB_TRACE_API, "SpDeleteContext returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status)));
return(KerbMapKerbNtStatusToNtStatus(Status));
}
//+-------------------------------------------------------------------------
//
// Function: KerbProcessTargetNames
//
// Synopsis: Takes the target names from both the negotiate hint and
// supplied by the caller and creates the real Kerberos target
// name.
//
// Effects:
//
// Arguments: TargetName - supplies the name passed in the TargetName
// parameter of InitializeSecurityContext.
// SuppTargetName - If present, an alternate name passed in
// a security token.
// Flags - flags to use when cracking a name. May be:
// KERB_CRACK_NAME_USE_WKSTA_REALM - if no realm can be
// determined, use the realm of the wksta
// KERB_CRACK_NAME_REALM_SUPPLIED - the caller has the
// realm name, so treat "@" in the name as a normal
// character, not a spacer.
// UseSpnRealmSupplied - If the target name was an spn and it
// contained a realm, it's set to TRUE
// FinalTarget - The processed name. Must be freed with KerbFreeKdcName.
// TargetRealm - If the name contains a realm portions, this is
// the realm. Should be freed using KerbFreeString.
//
//
// Requires:
//
// Returns:
//
// Notes: If the name has an "@" in it, it is converted into a standard
// Kerberos V5 name - everything after the "@" is put in the
// realm field, and everything before the @ is separted at the
// "/" characters into different pieces of the name. Depending
// on the number of pieces, it is passed as a KRB_NT_PRINCIPAL (1)
// or KRB_NT_SRV_INSTANCE (2), or KRB_NT_SRV_XHST (3+)
//
// If the name has an "\" in it, it is assumed to be an NT4
// name & put as is into a KRB_NT_MS_PRINCIPAL_NAME name
//
// If the name has neither a "\" or a "@" or a "/", it is
// assumed to be a simple name & passed as KRB_NT_PRINCIPAL
// in the caller's domain.
//
//
//
//--------------------------------------------------------------------------
#ifdef later
#define KERB_LOCALHOST_NAME L"localhost"
#endif
NTSTATUS
KerbProcessTargetNames(
IN PUNICODE_STRING TargetName,
IN OPTIONAL PUNICODE_STRING SuppTargetName,
IN ULONG Flags,
IN OUT PULONG ProcessFlags,
OUT PKERB_INTERNAL_NAME * FinalTarget,
OUT PUNICODE_STRING TargetRealm,
OUT OPTIONAL PKERB_SPN_CACHE_ENTRY * SpnCacheEntry
)
{
NTSTATUS Status = STATUS_SUCCESS;
USHORT NameType = 0;
PKERB_INTERNAL_NAME OutputName = NULL;
USHORT NameParts = 0, ExtraNameParts = 0;
ULONG NameLength = 0;
USHORT Index, NameIndex;
PUNICODE_STRING RealTargetName;
UNICODE_STRING SuppliedRealm = {0};
UNICODE_STRING EmptyString = {0};
UNICODE_STRING SidString = {0};
UNICODE_STRING FirstNamePart = {0};
PKERB_SPN_CACHE_ENTRY LocalCacheEntry = NULL;
#ifdef later
UNICODE_STRING LocalhostName = {0};
BOOLEAN ReplaceLocalhost = FALSE;
#endif
BOOLEAN DoneParsing = FALSE;
BOOLEAN PurgedEntry = FALSE;
PKERB_MIT_REALM MitRealm;
BOOLEAN UsedAlternateName;
PUCHAR Where;
PKERB_SID_CACHE_ENTRY CacheEntry = NULL;
*ProcessFlags = 0;
//
// If a supplemental target name was supplied, use it as it is more likely
// to be correct.
//
if (ARGUMENT_PRESENT(SuppTargetName) && (SuppTargetName->Length > 0))
{
RealTargetName = SuppTargetName;
}
else
{
RealTargetName = TargetName;
}
//
// If this is an IP address, we don't handle it so bail now.
//
if (KerbIsIpAddress(RealTargetName))
{
D_DebugLog((DEB_ERROR,"Ip address passed as target name: %wZ. Failing InitSecCtxt\n",
RealTargetName ));
Status = SEC_E_TARGET_UNKNOWN;
goto Cleanup;
}
#ifdef later
RtlInitUnicodeString(
&LocalhostName,
KERB_LOCALHOST_NAME
);
#endif
//
// Initialize the first part of the name to the whole string
//
FirstNamePart.Buffer = RealTargetName->Buffer;
FirstNamePart.Length = RealTargetName->Length;
FirstNamePart.MaximumLength = FirstNamePart.Length;
//
// Check the characters in the name. Search backwards to front because
// username may have "@" signs in them.
//
for ( Index = (RealTargetName->Length / sizeof(WCHAR)); Index-- > 0; )
{
switch(RealTargetName->Buffer[Index])
{
case L'@':
//
// If we have a realm name already, ignore this character.
//
if ((Flags & KERB_CRACK_NAME_REALM_SUPPLIED) != 0)
{
break;
}
//
// If we haven't hit any other separators, this is user@domain kind
// of name
//
if (NameType == 0)
{
NameType = KRB_NT_PRINCIPAL;
NameParts++;
SuppliedRealm.Buffer = &RealTargetName->Buffer[Index] + 1;
SuppliedRealm.Length = RealTargetName->Length - (Index + 1) * sizeof(WCHAR);
SuppliedRealm.MaximumLength = SuppliedRealm.Length;
if (SuppliedRealm.Length == 0)
{
Status = SEC_E_TARGET_UNKNOWN;
goto Cleanup;
}
FirstNamePart.Buffer = RealTargetName->Buffer;
FirstNamePart.Length = Index * sizeof(WCHAR);
FirstNamePart.MaximumLength = FirstNamePart.Length;
}
// else
// {
// Status = SEC_E_TARGET_UNKNOWN;
// goto Cleanup;
// }
break;
case L'/':
//
// All names that have a '/' separator are KRB_NT_SRV_INST
// If we saw an @before this, we need to do something special.
//
NameType = KRB_NT_SRV_INST;
NameParts ++;
break;
case '\\':
//
// If we have a realm name already, ignore this character.
//
if ((Flags & KERB_CRACK_NAME_REALM_SUPPLIED) != 0)
{
break;
}
//
// If we hit a backslash, this is an NT4 style name, so treat it
// as such.
// Just for error checking purposes, make sure that the current
// name type was 0
//
if (NameType != 0)
{
Status = SEC_E_TARGET_UNKNOWN;
goto Cleanup;
}
NameType = KRB_NT_MS_PRINCIPAL;
NameParts = 1;
SuppliedRealm.Buffer = RealTargetName->Buffer;
SuppliedRealm.Length = Index * sizeof(WCHAR);
SuppliedRealm.MaximumLength = SuppliedRealm.Length;
FirstNamePart.Buffer = &RealTargetName->Buffer[Index] + 1;
FirstNamePart.Length = RealTargetName->Length - (Index + 1) * sizeof(WCHAR);
FirstNamePart.MaximumLength = FirstNamePart.Length;
if (SuppliedRealm.Length == 0 || FirstNamePart.Length == 0)
{
Status = SEC_E_TARGET_UNKNOWN;
goto Cleanup;
}
DoneParsing = TRUE;
break;
default:
break;
}
if (DoneParsing)
{
break;
}
}
//
// If we didn't exit early, then we were sent a name with no "@" sign.
// If there were no separators, then it is a flat principal name
//
if (!DoneParsing && (NameType == 0))
{
if (NameParts == 0)
{
//
// The name has no separators, so it is a flat principal name
//
NameType = KRB_NT_PRINCIPAL;
NameParts = 1;
}
}
//
// For KRB_NT_SRV_INST, get the name parts correct and tell the caller
// that a realm was supplied in the spn
//
if (NameType == KRB_NT_SRV_INST)
{
if (SuppliedRealm.Length == 0)
{
// We have an spn of the form a/b..../n and the name parts needs
// to be upped by one
// If we had an spn of the form a/b@c, then the name
// parts would be right.
NameParts++;
}
else
{
// We need to filter this back to the caller so that the
// name canonicalization bit is not set.
*ProcessFlags |= KERB_GET_TICKET_NO_CANONICALIZE;
}
}
//
// Check for an MIT realm with the supplied realm - if the name type is
// KRB_NT_PRINCIPAL, send it as a UPN unless we can find an MIT realm.
// If we are not a member of a domain, then we can't default so use
// the domain name supplied. Also, if we are using supplied credentials
// we don't want to use the wksta realm.
//
if ((NameType == KRB_NT_PRINCIPAL) && (KerbGetGlobalRole() != KerbRoleStandalone) &&
((Flags & KERB_CRACK_NAME_USE_WKSTA_REALM) != 0))
{
BOOLEAN Result;
Result = KerbLookupMitRealm(
&SuppliedRealm,
&MitRealm,
&UsedAlternateName
);
//
// If we didn't find a realm, use this as a UPN
//
if (!Result)
{
//
// If the caller supplied the realm separately, then don't
// send it as a UPN.
//
if ((Flags & KERB_CRACK_NAME_REALM_SUPPLIED) == 0)
{
NameType = KRB_NT_ENTERPRISE_PRINCIPAL;
NameParts = 1;
FirstNamePart = *RealTargetName;
RtlInitUnicodeString(
&SuppliedRealm,
NULL
);
}
}
//
// For logon, its interesting to know if we're doing an MIT realm lookup
//
else
{
D_DebugLog((DEB_TRACE, "Using MIT realm in Process TargetName\n"));
*ProcessFlags |= KERB_MIT_REALM_USED;
}
}
NameLength = FirstNamePart.Length + NameParts * sizeof(WCHAR);
D_DebugLog((DEB_TRACE_CTXT,
"Parsed name %wZ (%wZ) into:\n\t name type 0x%x, name count %d, \n\t realm %wZ, \n\t first part %wZ\n",
TargetName,
SuppTargetName,
NameType,
NameParts,
&SuppliedRealm,
&FirstNamePart
));
#ifdef later
//
// If the name end in "localhost", replace it with our dns machine
// name.
//
if ((NameType == KRB_NT_SRV_INST) &&
(NameParts == 2) &&
(RealTargetName->Length > LocalhostName.Length) &&
RtlEqualMemory(
RealTargetName->Buffer + (RealTargetName->Length - LocalhostName.Length ) / sizeof(WCHAR),
LocalhostName.Buffer,
LocalhostName.Length
))
{
NameLength -= LocalhostName.Length;
KerbGlobalReadLock();
NameLength += KerbGlobalMitMachineServiceName->Names[1].Length;
//
// Set the flag to indicate we need to replace the name, and that
// the global lock is held.
//
ReplaceLocalhost = TRUE;
}
#endif
//
// Create the output names
//
OutputName = (PKERB_INTERNAL_NAME) KerbAllocate(KERB_INTERNAL_NAME_SIZE(NameParts + ExtraNameParts) + NameLength);
if (OutputName == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
OutputName->NameCount = NameParts + ExtraNameParts;
OutputName->NameType = NameType;
Where = (PUCHAR) OutputName + KERB_INTERNAL_NAME_SIZE(NameParts+ExtraNameParts);
NameIndex = 0;
//
// If there is only one part of the name, handle that first
//
if (NameParts == 1)
{
OutputName->Names[0].Length = FirstNamePart.Length;
OutputName->Names[0].MaximumLength = FirstNamePart.Length + sizeof(WCHAR);
OutputName->Names[0].Buffer = (LPWSTR) Where;
RtlCopyMemory(
Where,
FirstNamePart.Buffer,
FirstNamePart.Length
);
OutputName->Names[0].Buffer[FirstNamePart.Length / sizeof(WCHAR)] = L'\0';
Where += FirstNamePart.Length + sizeof(WCHAR);
NameIndex = 1;
}
else
{
UNICODE_STRING TempName;
//
// Build up the name, piece by piece
//
DoneParsing = FALSE;
NameIndex = 0;
TempName.Buffer = FirstNamePart.Buffer;
for ( Index = 0; Index <= FirstNamePart.Length / sizeof(WCHAR) ; Index++ )
{
//
// If we hit the end or a separator, build a name part
//
if ((Index == FirstNamePart.Length / sizeof(WCHAR)) ||
(FirstNamePart.Buffer[Index] == L'/') )
{
#ifdef later
if ((NameIndex == 1) && (ReplaceLocalhost))
{
OutputName->Names[NameIndex].Length = KerbGlobalMitMachineServiceName->Names[1].Length;
OutputName->Names[NameIndex].MaximumLength = OutputName->Names[NameIndex].Length + sizeof(WCHAR);
OutputName->Names[NameIndex].Buffer = (LPWSTR) Where;
RtlCopyMemory(
Where,
KerbGlobalMitMachineServiceName->Names[1].Buffer,
OutputName->Names[NameIndex].Length
);
//
// Release the lock now
//
KerbGlobalReleaseLock();
ReplaceLocalhost = FALSE;
}
else
#endif
{
OutputName->Names[NameIndex].Length = (USHORT) (&FirstNamePart.Buffer[Index] - TempName.Buffer) * sizeof(WCHAR);
OutputName->Names[NameIndex].MaximumLength = OutputName->Names[NameIndex].Length + sizeof(WCHAR);
OutputName->Names[NameIndex].Buffer = (LPWSTR) Where;
RtlCopyMemory(
Where,
TempName.Buffer,
OutputName->Names[NameIndex].Length
);
}
Where += OutputName->Names[NameIndex].Length;
*(LPWSTR)Where = L'\0';
Where += sizeof(WCHAR);
NameIndex++;
TempName.Buffer = &FirstNamePart.Buffer[Index+1];
}
}
DsysAssert(NameParts == NameIndex);
}
//
// Now that we've built the output name, check SPN Cache.
//
if ( ARGUMENT_PRESENT(SpnCacheEntry) &&
NameType == KRB_NT_SRV_INST &&
SuppliedRealm.Length == 0 )
{
LocalCacheEntry = KerbLocateSpnCacheEntry(OutputName);
if (NULL != LocalCacheEntry)
{
DebugLog((DEB_TRACE_SPN_CACHE, "Found in SPN Cache %p ", LocalCacheEntry));
D_KerbPrintKdcName(DEB_TRACE_SPN_CACHE, LocalCacheEntry->Spn);
*SpnCacheEntry = LocalCacheEntry;
LocalCacheEntry = NULL;
*ProcessFlags |= KERB_TARGET_USED_SPN_CACHE;
}
}
D_DebugLog((DEB_TRACE_CTXT,"Cracked name %wZ into: ", RealTargetName));
D_KerbPrintKdcName(DEB_TRACE_CTXT,OutputName);
if (((Flags & KERB_CRACK_NAME_USE_WKSTA_REALM) == 0) || SuppliedRealm.Length > 0)
{
Status = KerbDuplicateString(
TargetRealm,
&SuppliedRealm
);
}
else
{
if ((Flags & KERB_CRACK_NAME_USE_WKSTA_REALM) == 0)
{
DsysAssert(FALSE); // hey, this shouldn't ever be hit...
RtlInitUnicodeString(
TargetRealm,
NULL
);
}
else
{
//
// There was no realm name provided, so use the wksta domain
//
Status = KerbGetOurDomainName(
TargetRealm
);
}
}
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
*FinalTarget = OutputName;
OutputName = NULL;
Cleanup:
#ifdef later
if (ReplaceLocalhost)
{
KerbGlobalReleaseLock();
}
#endif
if ( LocalCacheEntry )
{
KerbDereferenceSpnCacheEntry( LocalCacheEntry );
}
if (OutputName != NULL)
{
KerbFree(OutputName);
}
return(Status);
}
//+-------------------------------------------------------------------------
//
// Function: KerbValidateChannelBindings
//
// Synopsis: Validates the channel bindings copied from the client.
//
// Effects:
//
// Arguments: pBuffer -- Input buffer that contains channel bindings
//
// Requires:
//
// Returns:
//
// Notes:
//
//--------------------------------------------------------------------------
NTSTATUS
KerbValidateChannelBindings(
IN PVOID pBuffer,
IN ULONG ulBufferLength
)
{
PSEC_CHANNEL_BINDINGS pClientBindings = (PSEC_CHANNEL_BINDINGS) pBuffer;
DWORD dwBindingLength;
DWORD dwInitiatorEnd;
DWORD dwAcceptorEnd;
DWORD dwApplicationEnd;
//
// If channel bindings were specified, they had better be there
//
if (pBuffer == NULL || ulBufferLength < sizeof(SEC_CHANNEL_BINDINGS))
{
return STATUS_INVALID_PARAMETER;
}
//
// Make sure we got one contiguous buffer
//
dwBindingLength = sizeof(SEC_CHANNEL_BINDINGS)
+ pClientBindings->cbInitiatorLength
+ pClientBindings->cbAcceptorLength
+ pClientBindings->cbApplicationDataLength;
//
// Make sure the lengths are valid and check for overflow
//
if (dwBindingLength > ulBufferLength)
{
return STATUS_INVALID_PARAMETER;
}
dwInitiatorEnd = pClientBindings->dwInitiatorOffset + pClientBindings->cbInitiatorLength;
dwAcceptorEnd = pClientBindings->dwAcceptorOffset + pClientBindings->cbAcceptorLength;
dwApplicationEnd = pClientBindings->dwApplicationDataOffset + pClientBindings->cbApplicationDataLength;
if ((dwInitiatorEnd > dwBindingLength || dwInitiatorEnd < pClientBindings->dwInitiatorOffset)
||
(dwAcceptorEnd > dwBindingLength || dwAcceptorEnd < pClientBindings->dwAcceptorOffset)
||
(dwApplicationEnd > dwBindingLength || dwApplicationEnd < pClientBindings->dwApplicationDataOffset))
{
return STATUS_INVALID_PARAMETER;
}
return STATUS_SUCCESS;
}
//+-------------------------------------------------------------------------
//
// Function: SpInitLsaModeContext
//
// Synopsis: Kerberos implementation of InitializeSecurityContext. This
// routine handles the client side of authentication by
// acquiring a ticket to the specified target. If a context
// handle is passed in, then the input buffer is used to
// verify the authenticity of the server.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpInitLsaModeContext(
IN OPTIONAL LSA_SEC_HANDLE CredentialHandle,
IN OPTIONAL LSA_SEC_HANDLE ContextHandle,
IN OPTIONAL PUNICODE_STRING TargetName,
IN ULONG ContextRequirements,
IN ULONG TargetDataRep,
IN PSecBufferDesc InputBuffers,
OUT PLSA_SEC_HANDLE NewContextHandle,
IN OUT PSecBufferDesc OutputBuffers,
OUT PULONG ContextAttributes,
OUT PTimeStamp ExpirationTime,
OUT PBOOLEAN MappedContext,
OUT PSecBuffer ContextData
)
{
PKERB_LOGON_SESSION LogonSession = NULL;
PKERB_CREDENTIAL Credential = NULL;
PKERB_TICKET_CACHE_ENTRY TicketCacheEntry = NULL;
ULONG TicketOptions = 0;
NTSTATUS Status = STATUS_SUCCESS;
LUID LogonId;
PUCHAR Request = NULL;
ULONG RequestSize = 0;
PUCHAR Reply = NULL;
ULONG ReplySize;
PSecBuffer OutputToken = NULL;
PSecBuffer InputToken = NULL;
UNICODE_STRING LocalTargetName;
UNICODE_STRING TargetDomainName;
PKERB_INTERNAL_NAME TargetInternalName = NULL;
ULONG Index;
PKERB_CONTEXT Context = NULL;
ULONG Nonce = 0;
ULONG ProcessFlags = 0;
ULONG ReceiveNonce = 0;
ULONG ContextFlags = 0;
ULONG ContextAttribs = 0;
TimeStamp ContextLifetime;
BOOLEAN DoThirdLeg = FALSE;
BOOLEAN GetAuthTicket = FALSE;
BOOLEAN UseNullSession = FALSE;
BOOLEAN GetServerTgt = FALSE;
PKERB_ERROR ErrorMessage = NULL;
PKERB_ERROR_METHOD_DATA ErrorData = NULL;
PKERB_EXT_ERROR pExtendedError = NULL;
PKERB_TGT_REPLY TgtReply = NULL;
PKERB_SPN_CACHE_ENTRY SpnCacheEntry = NULL;
ULONG ContextRetries = 0;
KERB_CONTEXT_STATE ContextState = InvalidState;
KERB_ENCRYPTION_KEY SubSessionKey = {0};
BOOLEAN ClientAskedForDelegate = FALSE, ClientAskedForDelegateIfSafe = FALSE;
ULONG ClientProcess = 0;
PKERB_CREDMAN_CRED CredManCredentials = NULL;
NTSTATUS InitialStatus = STATUS_SUCCESS;
PSEC_CHANNEL_BINDINGS pChannelBindings = NULL;
PBYTE pbMarshalledTargetInfo = NULL;
ULONG cbMarshalledTargetInfo = 0;
LUID NetworkServiceLuid = NETWORKSERVICE_LUID;
KERB_INITSC_INFO InitSCTraceInfo;
InitSCTraceInfo.EventTrace.Size = 0;
D_DebugLog((DEB_TRACE_API,"SpInitLsaModeContext 0x%x called\n",ContextHandle));
if( KerbEventTraceFlag ) // Event Trace: KerbInitSecurityContextStart {No Data}
{
InitSCTraceInfo.EventTrace.Guid = KerbInitSCGuid;
InitSCTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_START;
InitSCTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID;
InitSCTraceInfo.EventTrace.Size = sizeof (EVENT_TRACE_HEADER);
TraceEvent(
KerbTraceLoggerHandle,
(PEVENT_TRACE_HEADER)&InitSCTraceInfo
);
}
//
// Initialize the outputs.
//
*ContextAttributes = 0;
*NewContextHandle = 0;
*ExpirationTime = KerbGlobalHasNeverTime;
*MappedContext = FALSE;
ContextData->pvBuffer = NULL;
ContextData->cbBuffer = 0;
LocalTargetName.Buffer = NULL;
LocalTargetName.Length = 0;
TargetDomainName.Buffer = NULL;
TargetDomainName.Length = 0;
if (!KerbGlobalInitialized)
{
Status = STATUS_INVALID_SERVER_STATE;
goto Cleanup;
}
//
// Make sure we have at least one ip address
//
KerbGlobalReadLock();
if (KerbGlobalNoTcpUdp)
{
Status = STATUS_NETWORK_UNREACHABLE;
}
KerbGlobalReleaseLock();
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Delegate will mean delegate_if_safe for this
// release (NT5)
//
if ( ContextRequirements & ISC_REQ_DELEGATE )
{
ClientAskedForDelegate = TRUE;
ContextRequirements |= ISC_REQ_DELEGATE_IF_SAFE ;
ContextRequirements &= ~(ISC_REQ_DELEGATE) ;
}
else if ( ContextRequirements & ISC_REQ_DELEGATE_IF_SAFE )
{
ClientAskedForDelegateIfSafe = TRUE;
}
//////////////////////////////////////////////////////////////////////
//
// Process the input tokens
//
/////////////////////////////////////////////////////////////////////
//
// First locate the output token.
//
for (Index = 0; Index < OutputBuffers->cBuffers ; Index++ )
{
if (BUFFERTYPE(OutputBuffers->pBuffers[Index]) == SECBUFFER_TOKEN)
{
OutputToken = &OutputBuffers->pBuffers[Index];
Status = LsaFunctions->MapBuffer(OutputToken,OutputToken);
break;
}
}
if (!NT_SUCCESS(Status))
{
D_DebugLog((DEB_ERROR,"Failed to map output token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// Now locate the Input token.
//
for (Index = 0; Index < InputBuffers->cBuffers ; Index++ )
{
if (BUFFERTYPE(InputBuffers->pBuffers[Index]) == SECBUFFER_TOKEN)
{
InputToken = &InputBuffers->pBuffers[Index];
Status = LsaFunctions->MapBuffer(InputToken,InputToken);
break;
}
}
if (!NT_SUCCESS(Status))
{
D_DebugLog((DEB_ERROR,"Failed to map Input token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// Check to see if we were passed an additional name
//
for (Index = 0; Index < InputBuffers->cBuffers ; Index++ )
{
if (BUFFERTYPE(InputBuffers->pBuffers[Index]) == SECBUFFER_NEGOTIATION_INFO)
{
Status = LsaFunctions->MapBuffer(
&InputBuffers->pBuffers[Index],
&InputBuffers->pBuffers[Index]
);
if (!NT_SUCCESS(Status))
{
D_DebugLog( (DEB_ERROR, "Failed to map incoming SECBUFFER_NEGOTIATION_INFO. %x, %ws, %d", Status, THIS_FILE, __LINE__) );
goto Cleanup;
}
LocalTargetName.Buffer = (LPWSTR) InputBuffers->pBuffers[Index].pvBuffer;
//
// We can only stick 64k in a the length field, so make sure the
// buffer is not too big.
//
if (InputBuffers->pBuffers[Index].cbBuffer > 0xffff)
{
Status = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
LocalTargetName.Length =
LocalTargetName.MaximumLength = (USHORT) InputBuffers->pBuffers[Index].cbBuffer;
break;
}
}
//
// Process the target names
//
Status = KerbProcessTargetNames(
TargetName,
&LocalTargetName,
0, // No flags
&ProcessFlags,
&TargetInternalName,
&TargetDomainName,
&SpnCacheEntry
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Check to see if we were passed channel bindings
//
for( Index = 0; Index < InputBuffers->cBuffers; Index++ )
{
if( BUFFERTYPE(InputBuffers->pBuffers[Index]) == SECBUFFER_CHANNEL_BINDINGS )
{
PVOID temp = NULL;
Status = LsaFunctions->MapBuffer(
&InputBuffers->pBuffers[Index],
&InputBuffers->pBuffers[Index]
);
if( !NT_SUCCESS(Status) )
{
D_DebugLog( (DEB_ERROR,
"Failed to map incoming SECBUFFER_CHANNEL_BINDINGS. %x, %ws, %d\n",
Status,
THIS_FILE,
__LINE__) );
goto Cleanup;
}
pChannelBindings = (PSEC_CHANNEL_BINDINGS) InputBuffers->pBuffers[Index].pvBuffer;
Status = KerbValidateChannelBindings(pChannelBindings,
InputBuffers->pBuffers[Index].cbBuffer);
if (!NT_SUCCESS(Status))
{
pChannelBindings = NULL;
goto Cleanup;
}
break;
}
}
//////////////////////////////////////////////////////////////////////
//
// If the caller passed in a context handle, deal with updating an
// existing context.
//
/////////////////////////////////////////////////////////////////////
//
// If the input context handle is no NULL then we are actually
// finalizing an already-existing context. So be it.
//
//
// Use "while" so we can break out.
//
while (ContextHandle != 0)
{
D_DebugLog((DEB_TRACE_CTXT,"SpInitLsaModeContext: Second call to Initialize\n"));
if (InputToken == NULL)
{
D_DebugLog((DEB_ERROR,"Trying to complete a context with no input token! %ws, line %d\n", THIS_FILE, __LINE__));
Status = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// First reference the context.
//
Status = KerbReferenceContext(
ContextHandle,
FALSE, // don't unlink
&Context
);
if (Context == NULL)
{
D_DebugLog((DEB_ERROR,"Failed to reference context 0x%x. %ws, line %d\n",ContextHandle, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// Check the mode of the context to make sure we can finalize it.
//
KerbReadLockContexts();
ContextState = Context->ContextState;
if ((ContextState != ApRequestSentState) &&
(ContextState != TgtRequestSentState))
{
D_DebugLog((DEB_ERROR,"Invalid context state: %d. %ws, line %d\n",
Context->ContextState, THIS_FILE, __LINE__));
Status = STATUS_INVALID_HANDLE;
KerbUnlockContexts();
goto Cleanup;
}
ContextRetries = Context->Retries;
ContextFlags = Context->ContextFlags;
ContextAttribs = Context->ContextAttributes;
Nonce = Context->Nonce;
CredentialHandle = Context->CredentialHandle;
ClientProcess = Context->ClientProcess;
KerbUnlockContexts();
//
// If we are not doing datagram, unpack the AP or TGT reply.
//
if ((ContextFlags & ISC_RET_DATAGRAM) == 0)
{
////////////////////////////////////////////////////
//
// Handle a TGT reply - get out the TGT & the name of
// the server
//
////////////////////////////////////////////////////
if (ContextState == TgtRequestSentState)
{
D_DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext calling KerbUnpackTgtReply\n"));
KerbWriteLockContexts();
if (!(Context->ContextAttributes & KERB_CONTEXT_USER_TO_USER))
{
Context->ContextAttributes |= KERB_CONTEXT_USER_TO_USER;
DebugLog((DEB_WARN, "SpInitLsaModeContext * use_sesion_key but USER2USER-OUTBOUND not set, added it now\n"));
}
KerbUnlockContexts();
Status = KerbUnpackTgtReply(
Context,
(PUCHAR) InputToken->pvBuffer,
InputToken->cbBuffer,
&TargetInternalName,
&TargetDomainName,
&TgtReply
);
if (!NT_SUCCESS(Status))
{
D_DebugLog((DEB_ERROR,"Failed to unpack TGT reply: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
//
// Check for an error message
//
Status = KerbReceiveErrorMessage(
(PUCHAR) InputToken->pvBuffer,
InputToken->cbBuffer,
Context,
&ErrorMessage,
&ErrorData
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
KerbReportKerbError(
NULL,
NULL,
NULL,
NULL,
KLIN(FILENO,__LINE__),
ErrorMessage,
((NULL != ErrorMessage) ? ErrorMessage->error_code : KRB_ERR_GENERIC),
pExtendedError,
FALSE
);
//
// Ahh. We have an error message. See if it is an
// error we can handle, and if so, Now we need to
// try to build a better AP request. Or, if we have
// already retried once, fail.
//
DebugLog((DEB_WARN, "SpInitLsaModeContext received KERB_ERROR message with error 0x%x, can't handle\n",
ErrorMessage->error_code ));
if ((ErrorMessage->error_code == KRB_ERR_GENERIC)
&& (ErrorData != NULL)
&& (ErrorData->bit_mask & data_value_present)
&& (ErrorData->data_type == KERB_AP_ERR_TYPE_NTSTATUS)
&& (ErrorData->data_value.length == sizeof(ULONG)))
{
Status = *((PULONG)ErrorData->data_value.value);
}
else
{
Status = KerbMapKerbError((KERBERR) ErrorMessage->error_code);
if (NT_SUCCESS(Status))
{
Status = STATUS_INTERNAL_ERROR;
}
}
goto Cleanup;
}
//
// Break out so we generate a normal request now
//
break;
}
else // not user-to-user
{
////////////////////////////////////////////////////
//
// This is the response to an AP request. It could be
// an AP reply or an error. Handle both cases
//
////////////////////////////////////////////////////
//
// Now unpack the AP reply
//
Status = KerbVerifyApReply(
Context,
(PUCHAR) InputToken->pvBuffer,
InputToken->cbBuffer,
&ReceiveNonce
);
if (!NT_SUCCESS(Status))
{
//
// Check for an error message
//
Status = KerbReceiveErrorMessage(
(PUCHAR) InputToken->pvBuffer,
InputToken->cbBuffer,
Context,
&ErrorMessage,
&ErrorData
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
KerbReportKerbError(
NULL,
NULL,
NULL,
NULL,
KLIN(FILENO, __LINE__),
ErrorMessage,
((NULL != ErrorMessage) ? ErrorMessage->error_code : KRB_ERR_GENERIC),
pExtendedError,
FALSE
);
DebugLog((DEB_WARN,"Failed to verify AP reply: 0x%x\n",ErrorMessage->error_code));
//
// Ahh. We have an error message. See if it is an
// error we can handle, and if so, Now we need to
// try to build a better AP request. Or, if we have
// already retried once, fail. We can't get a new ticket
// with user-to-user.
//
if ((ContextRetries != 0) ||
(((KERBERR) ErrorMessage->error_code != KRB_AP_ERR_SKEW) &&
((KERBERR) ErrorMessage->error_code != KRB_AP_ERR_TKT_NYV) &&
((KERBERR) ErrorMessage->error_code != KRB_AP_ERR_USER_TO_USER_REQUIRED) &&
((KERBERR) ErrorMessage->error_code != KRB_AP_ERR_MODIFIED)) ||
(((KERBERR) ErrorMessage->error_code == KRB_AP_ERR_MODIFIED) &&
((ContextAttribs & KERB_CONTEXT_USER_TO_USER) != 0)))
{
if ((ErrorMessage->error_code == KRB_ERR_GENERIC) &&
(ErrorData != NULL) && (ErrorData->data_type == KERB_AP_ERR_TYPE_NTSTATUS) &&
((ErrorData->bit_mask & data_value_present) != 0) &&
(ErrorData->data_value.value != NULL) &&
(ErrorData->data_value.length == sizeof(ULONG)))
{
Status = *((PULONG)ErrorMessage->error_data.value);
if (NT_SUCCESS(Status))
{
Status = STATUS_INTERNAL_ERROR;
}
}
if (ErrorMessage->error_code == KRB_AP_ERR_MODIFIED)
{
//
// If the server couldn't decrypt the ticket,
// then the target name is wrong for the server.
//
DebugLog((DEB_ERROR, "App modified error (NO CONTINUE, bail)\n"));
KerbReportApError(ErrorMessage);
Status = SEC_E_WRONG_PRINCIPAL;
}
else if (ErrorMessage->error_code == KRB_AP_ERR_TKT_NYV)
{
DebugLog((DEB_ERROR, "Not yet valid error - Check Time Skew\n"));
KerbReportApError(ErrorMessage);
Status = STATUS_TIME_DIFFERENCE_AT_DC;
}
else
{
DebugLog((DEB_ERROR, "InitSecContext Received KERB_ERROR message with error 0x%x, can't handle. %ws, line %d\n",
ErrorMessage->error_code, THIS_FILE, __LINE__ ));
Status = KerbMapKerbError((KERBERR) ErrorMessage->error_code);
if (NT_SUCCESS(Status))
{
Status = STATUS_INTERNAL_ERROR;
}
}
goto Cleanup;
}
else
{
//
// Check to see if the server supports skew
//
if ((ErrorMessage->error_code == KRB_AP_ERR_SKEW) &&
(ErrorData == NULL) || ((ErrorData != NULL) && (ErrorData->data_type != KERB_AP_ERR_TYPE_SKEW_RECOVERY)))
{
//
// The server doesn't support skew recovery.
//
D_DebugLog((DEB_WARN,"Skew error but server doesn't handle skew recovery.\n"));
Status = KerbMapKerbError((KERBERR) ErrorMessage->error_code);
goto Cleanup;
}
//
// Here's where we'll punt "not yet valid tickets" and friends...
//
if (ErrorMessage->error_code == KRB_AP_ERR_TKT_NYV)
{
KerbPurgeServiceTicketAndTgt(
Context,
CredentialHandle,
CredManCredentials
);
DebugLog((DEB_ERROR, "Purged all tickets due to NYV error!\n"));
}
KerbWriteLockContexts();
Context->Retries++;
KerbUnlockContexts();
}
////////////////////////////////////////////////////
//
// We got an error we can handle. For user-to-user
// required, we received the TGT so we can get
// the appropriate ticket. For modified, we want
// to toss the old ticket & get a new one, hopefully
// with a better key.
//
////////////////////////////////////////////////////
if ((KERBERR) ErrorMessage->error_code == KRB_AP_ERR_USER_TO_USER_REQUIRED)
{
D_DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext received KRB_AP_ERR_USER_TO_USER_REQUIRED\n"));
if ((ErrorMessage->bit_mask & error_data_present) == 0)
{
DebugLog((DEB_ERROR,"Server requires user-to-user but didn't send TGT reply. %ws, line %d\n", THIS_FILE, __LINE__));
Status = STATUS_NO_TGT_REPLY;
goto Cleanup;
}
//
// Check for TGT reply
//
Status = KerbUnpackTgtReply(
Context,
ErrorMessage->error_data.value,
ErrorMessage->error_data.length,
&TargetInternalName,
&TargetDomainName,
&TgtReply
);
if (!NT_SUCCESS(Status))
{
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
//
// Fall through into normal ticket handling
//
ContextFlags |= ISC_RET_USE_SESSION_KEY;
//
// Remove the old ticket cache entry
//
KerbWriteLockContexts();
TicketCacheEntry = Context->TicketCacheEntry;
Context->TicketCacheEntry = NULL;
KerbUnlockContexts();
if (TicketCacheEntry != NULL)
{
KerbFreeString(
&TargetDomainName
);
KerbFreeKdcName(
&TargetInternalName
);
//
// Get the target name from the old ticket
//
KerbReadLockTicketCache();
Status = KerbDuplicateString(
&TargetDomainName,
&TicketCacheEntry->DomainName
);
if (NT_SUCCESS(Status))
{
Status = KerbDuplicateKdcName(
&TargetInternalName,
TicketCacheEntry->ServiceName
);
}
KerbUnlockTicketCache();
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Free this ticket cache entry
//
KerbRemoveTicketCacheEntry(TicketCacheEntry);
//
// Remove the reference holding it to the context
//
KerbDereferenceTicketCacheEntry(TicketCacheEntry);
TicketCacheEntry = NULL;
}
break;
}
else if ((KERBERR) ErrorMessage->error_code == KRB_AP_ERR_MODIFIED)
{
DebugLog((DEB_WARN, "App modified error (purge ticket!)\n"));
KerbWriteLockContexts();
TicketCacheEntry = Context->TicketCacheEntry;
Context->TicketCacheEntry = NULL;
KerbUnlockContexts();
if (TicketCacheEntry != NULL)
{
//
// Get rid of the old ticket in the context
KerbFreeString(
&TargetDomainName
);
KerbFreeKdcName(
&TargetInternalName
);
//
// Get the target name from the old ticket
//
KerbReadLockTicketCache();
Status = KerbDuplicateString(
&TargetDomainName,
&TicketCacheEntry->DomainName
);
if (NT_SUCCESS(Status))
{
Status = KerbDuplicateKdcName(
&TargetInternalName,
TicketCacheEntry->ServiceName
);
}
KerbUnlockTicketCache();
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Free this ticket cache entry
//
KerbRemoveTicketCacheEntry(TicketCacheEntry);
//
// Remove the reference holding it to the context
//
KerbDereferenceTicketCacheEntry(TicketCacheEntry);
TicketCacheEntry = NULL;
}
}
break;
}
}
////////////////////////////////////////////////////
//
// We successfully decrypted the AP reply. At this point
// we want to finalize the context. For DCE style we send
// a useless AP reply to the server.
//
////////////////////////////////////////////////////
//
// If the caller wanted DCE style authentication, build another
// AP reply
//
KerbWriteLockContexts();
if ((Context->ContextFlags & ISC_RET_USED_DCE_STYLE) != 0)
{
DoThirdLeg = TRUE;
}
else
{
DoThirdLeg = FALSE;
}
Context->ReceiveNonce = ReceiveNonce;
KerbUnlockContexts();
if (DoThirdLeg)
{
//
// Build an AP reply to send back to the server.
//
Status = KerbBuildThirdLegApReply(
Context,
ReceiveNonce,
&Reply,
&ReplySize
);
if (!NT_SUCCESS(Status))
{
D_DebugLog((DEB_ERROR,"Failed to build AP reply: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
if (OutputToken == NULL)
{
D_DebugLog((DEB_ERROR,"Output token missing. %ws, line %d\n", THIS_FILE, __LINE__));
Status = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Return the AP reply in the output buffer.
//
if ((ContextRequirements & ISC_REQ_ALLOCATE_MEMORY) == 0)
{
if (OutputToken->cbBuffer < ReplySize)
{
ULONG ErrorData[3];
ErrorData[0] = ReplySize;
ErrorData[1] = OutputToken->cbBuffer;
ErrorData[2] = ClientProcess;
DebugLog((DEB_ERROR,"Output token is too small - sent in %d, needed %d. %ws, line %d\n",
OutputToken->cbBuffer,ReplySize, THIS_FILE, __LINE__ ));
OutputToken->cbBuffer = ReplySize;
Status = STATUS_BUFFER_TOO_SMALL;
KerbReportNtstatus(
KERBEVT_INSUFFICIENT_TOKEN_SIZE,
Status,
NULL,
0,
ErrorData,
3
);
goto Cleanup;
}
RtlCopyMemory(
OutputToken->pvBuffer,
Reply,
ReplySize
);
}
else
{
OutputToken->pvBuffer = Reply;
Reply = NULL;
*ContextAttributes |= ISC_RET_ALLOCATED_MEMORY;
}
OutputToken->cbBuffer = ReplySize;
}
else
{
//
// No return message, so set the return length to zero.
//
if (OutputToken != NULL)
{
OutputToken->cbBuffer = 0;
}
}
}
else
{
//////////////////////////////////////////////////////////////////////
//
// We are doing datagram, so we don't expect anything from the
// server but perhaps an error. If we get an error, handle it.
// Otherwise, build an AP request to send to the server
//
/////////////////////////////////////////////////////////////////////
//
// We are doing datagram. Build the AP request for the
// server side.
//
//
// Check for an error message
//
if ((InputToken != NULL) && (InputToken->cbBuffer != 0))
{
Status = KerbReceiveErrorMessage(
(PUCHAR) InputToken->pvBuffer,
InputToken->cbBuffer,
Context,
&ErrorMessage,
&ErrorData
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
KerbReportKerbError(
NULL,
NULL,
NULL,
NULL,
KLIN(FILENO, __LINE__),
ErrorMessage,
((NULL != ErrorMessage) ? ErrorMessage->error_code : KRB_ERR_GENERIC),
pExtendedError,
FALSE
);
//
// Check for a real error
//
if ((ErrorData != NULL) && (ErrorData->data_type == KERB_AP_ERR_TYPE_NTSTATUS) &&
((ErrorData->bit_mask & data_value_present) != 0) &&
(ErrorData->data_value.value != NULL) &&
(ErrorData->data_value.length == sizeof(ULONG)))
{
Status = *((PULONG)ErrorMessage->error_data.value);
if (NT_SUCCESS(Status))
{
Status = STATUS_INTERNAL_ERROR;
}
goto Cleanup;
}
}
//
// Get the associated credential
//
Status = KerbReferenceCredential(
CredentialHandle,
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.
//
LogonId = Credential->LogonId;
//
// Get the logon session
//
LogonSession = KerbReferenceLogonSession( &LogonId, FALSE );
if (LogonSession == NULL)
{
Status = STATUS_NO_SUCH_LOGON_SESSION;
goto Cleanup;
}
KerbReadLockLogonSessions(LogonSession);
if (Credential->SuppliedCredentials != NULL)
{
GetAuthTicket = TRUE;
} else if (((Credential->CredentialFlags & KERB_CRED_NULL_SESSION) != 0) ||
((ContextRequirements & ISC_REQ_NULL_SESSION) != 0))
{
UseNullSession = TRUE;
ContextFlags |= ISC_RET_NULL_SESSION;
}
KerbUnlockLogonSessions(LogonSession);
KerbReadLockContexts();
TicketCacheEntry = Context->TicketCacheEntry;
//
// Get the session key to use from the context
//
if (Context->SessionKey.keyvalue.value != 0)
{
if (!KERB_SUCCESS(KerbDuplicateKey(
&SubSessionKey,
&Context->SessionKey
)))
{
Status = STATUS_INSUFFICIENT_RESOURCES;
}
}
Context->TicketCacheEntry = NULL;
KerbUnlockContexts();
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
D_DebugLog((DEB_TRACE_CTXT2,"Building AP request for datagram oriented context\n"));
//
// If we are building a null session, build the special null
// session AP request
//
if (UseNullSession)
{
Status = KerbBuildNullSessionApRequest(
&Request,
&RequestSize
);
//
// Turn off all unsupported flags
//
ContextFlags &= ( ISC_RET_ALLOCATED_MEMORY |
ISC_RET_CONNECTION |
ISC_RET_DATAGRAM |
ISC_RET_NULL_SESSION );
}
else
{
if (TicketCacheEntry == NULL)
{
DebugLog((DEB_ERROR, "SpInitLsaModeContext does have service ticket\n"));
Status = STATUS_INTERNAL_ERROR;
goto Cleanup;
}
Status = KerbBuildApRequest(
LogonSession,
Credential,
CredManCredentials,
TicketCacheEntry,
ErrorMessage,
ContextAttribs,
&ContextFlags,
&Request,
&RequestSize,
&Nonce,
&SubSessionKey,
pChannelBindings
);
}
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to build AP request: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// Return the AP request in the output buffer.
//
if ((ContextRequirements & ISC_REQ_ALLOCATE_MEMORY) == 0)
{
if (OutputToken->cbBuffer < RequestSize)
{
ULONG ErrorData[3];
ErrorData[0] = RequestSize;
ErrorData[1] = OutputToken->cbBuffer;
ErrorData[2] = ClientProcess;
D_DebugLog((DEB_ERROR,"Output token is too small - sent in %d, needed %d. %ws, line %d\n",
OutputToken->cbBuffer,RequestSize, THIS_FILE, __LINE__ ));
OutputToken->cbBuffer = RequestSize;
Status = STATUS_BUFFER_TOO_SMALL;
KerbReportNtstatus(
KERBEVT_INSUFFICIENT_TOKEN_SIZE,
Status,
NULL,
0,
ErrorData,
3
);
goto Cleanup;
}
RtlCopyMemory(
OutputToken->pvBuffer,
Request,
RequestSize
);
}
else
{
OutputToken->pvBuffer = Request;
*ContextAttributes |= ISC_RET_ALLOCATED_MEMORY;
Request = NULL;
}
OutputToken->cbBuffer = RequestSize;
}
//
//
// We're done - we finalized.
//
Status = STATUS_SUCCESS;
KerbReadLockContexts();
Context->ContextFlags = ContextFlags;
*ContextAttributes |= Context->ContextFlags;
KerbUtcTimeToLocalTime(
ExpirationTime,
&Context->Lifetime
);
*NewContextHandle = ContextHandle;
KerbUnlockContexts();
goto Cleanup;
}
//////////////////////////////////////////////////////////////////////
//
// We need to create a request to the server, possibly a TGT request
// or an AP request depending on what phase of the protocol we're in.
//
/////////////////////////////////////////////////////////////////////
DsysAssert(!((Context != NULL) ^ ((ErrorMessage != NULL) || (TgtReply != NULL))));
D_DebugLog((DEB_TRACE_CTXT,"SpInitLsaModeContext: First call to Initialize\n"));
//
// This is the case where we are constructing a new context.
//
//
// Get the associated credential and its TGT, if needed
//
Status = KerbReferenceCredential(
CredentialHandle,
KERB_CRED_OUTBOUND | KERB_CRED_TGT_AVAIL,
FALSE,
&Credential
);
if (!NT_SUCCESS(Status))
{
InitialStatus = Status;
Status = KerbReferenceCredential(
CredentialHandle,
KERB_CRED_OUTBOUND,
FALSE,
&Credential
);
if( !NT_SUCCESS( Status ) || Credential->SuppliedCredentials != NULL)
{
Status = InitialStatus;
D_DebugLog((DEB_WARN,"Failed to locate credential 0x%x\n",Status));
goto Cleanup;
}
//
// if we got here, only explicit or credman creds are allowed.
// If the explicit creds failed to get a TGT from above, its also
// time to bail, as explicit creds never should fall back to credman.
//
}
//
// Get the logon id from the credentials so we can locate the
// logon session.
//
LogonId = Credential->LogonId;
//
// Get the logon session
//
LogonSession = KerbReferenceLogonSession( &LogonId, FALSE );
if (LogonSession == NULL)
{
Status = STATUS_NO_SUCH_LOGON_SESSION;
goto Cleanup;
}
KerbWriteLockLogonSessions(LogonSession);
if (Credential->SuppliedCredentials != NULL)
{
GetAuthTicket = TRUE;
// Ignore SPN cache for supplied credentials
ProcessFlags &= ~KERB_TARGET_UNKNOWN_SPN;
}
else if (((Credential->CredentialFlags & KERB_CRED_NULL_SESSION) != 0) ||
((ContextRequirements & ISC_REQ_NULL_SESSION) != 0))
{
UseNullSession = TRUE;
ContextFlags |= ISC_RET_NULL_SESSION;
}
else
{
//
// go to the credential manager to try and find
// credentials for this specific target
//
ULONG ExtraTargetFlags = 0;
if ((ContextRequirements & ISC_REQ_USE_SUPPLIED_CREDS) != 0)
{
ExtraTargetFlags = CRED_TI_ONLY_PASSWORD_REQUIRED;
}
Status = KerbCheckCredMgrForGivenTarget(
LogonSession,
Credential,
TargetName, // original targetname, may contain marshalled targetinfo
TargetInternalName,
ExtraTargetFlags,
&TargetDomainName,
NULL,
&CredManCredentials,
&pbMarshalledTargetInfo,
&cbMarshalledTargetInfo
);
if (!NT_SUCCESS(Status))
{
KerbUnlockLogonSessions(LogonSession);
D_DebugLog((DEB_ERROR,"Failed to get outbound ticket: 0x%x\n",Status));
goto Cleanup;
}
if (CredManCredentials != NULL)
{
GetAuthTicket = TRUE;
ProcessFlags &= ~KERB_TARGET_UNKNOWN_SPN;
}
else
{
//
// if this is a local account logon then we have to have a cred man
// credential
//
if ((Credential->CredentialFlags & KERB_CRED_LOCAL_ACCOUNT) != 0)
{
KerbUnlockLogonSessions(LogonSession);
D_DebugLog((DEB_WARN, "Trying to use a local logon session with Kerberos\n"));
Status = SEC_E_NO_CREDENTIALS;
goto Cleanup;
}
//
// if no credman cred was found, we didn't use explicit creds,
// and the initial credential reference for TGT_AVAIL failed, bail now.
//
if( !NT_SUCCESS( InitialStatus ) )
{
KerbUnlockLogonSessions(LogonSession);
Status = InitialStatus;
D_DebugLog((DEB_WARN,"Failed to locate credential 0x%x\n",Status));
goto Cleanup;
}
}
}
#if DBG
D_DebugLog((DEB_TRACE_CTXT, "SpInitLsaModeContext: Initailizing context for %wZ\\%wZ\n",
&LogonSession->PrimaryCredentials.DomainName,
&LogonSession->PrimaryCredentials.UserName ));
#endif
KerbUnlockLogonSessions(LogonSession);
//////////////////////////////////////////////////////////////////////
//
// Process all the context requirements. We don't support all of them
// and some of them are mutually exclusive. In general, we don't fail
// if we're asked to do something we can't do, unless it seems mandatory,
// like allocating memory.
//
/////////////////////////////////////////////////////////////////////
//
// Figure out the context flags
//
if ((ContextRequirements & ISC_REQ_MUTUAL_AUTH) != 0)
{
D_DebugLog((DEB_TRACE_CTXT,"Client wants mutual auth.\n"));
ContextFlags |= ISC_RET_MUTUAL_AUTH;
}
if ((ContextRequirements & ISC_REQ_SEQUENCE_DETECT) != 0)
{
D_DebugLog((DEB_TRACE_CTXT, "Client wants sequence detect\n"));
ContextFlags |= ISC_RET_SEQUENCE_DETECT | ISC_RET_INTEGRITY;
}
if ((ContextRequirements & ISC_REQ_REPLAY_DETECT) != 0)
{
D_DebugLog((DEB_TRACE_CTXT, "Client wants replay detect\n"));
ContextFlags |= ISC_RET_REPLAY_DETECT | ISC_RET_INTEGRITY;
}
if ((ContextRequirements & ISC_REQ_INTEGRITY) != 0)
{
D_DebugLog((DEB_TRACE_CTXT, "Client wants integrity\n"));
ContextFlags |= ISC_RET_INTEGRITY;
}
if ((ContextRequirements & ISC_REQ_CONFIDENTIALITY) != 0)
{
D_DebugLog((DEB_TRACE_CTXT, "Client wants privacy\n"));
ContextFlags |= (ISC_RET_CONFIDENTIALITY |
ISC_RET_INTEGRITY |
ISC_RET_SEQUENCE_DETECT |
ISC_RET_REPLAY_DETECT );
}
if ((ContextRequirements & ISC_REQ_USE_DCE_STYLE) != 0)
{
D_DebugLog((DEB_TRACE_CTXT, "Client wants DCE style\n"));
ContextFlags |= ISC_RET_USED_DCE_STYLE;
}
if ((ContextRequirements & ISC_REQ_EXTENDED_ERROR) != 0)
{
D_DebugLog((DEB_TRACE_CTXT, "Client wants extended error\n"));
ContextFlags |= ISC_RET_EXTENDED_ERROR;
}
if ((ContextRequirements & ISC_REQ_DATAGRAM) != 0)
{
if ((ContextRequirements & ISC_REQ_CONNECTION) != 0)
{
D_DebugLog((DEB_ERROR,"Client wanted both data gram and connection. %ws, line %d\n", THIS_FILE, __LINE__));
Status = SEC_E_UNSUPPORTED_FUNCTION;
goto Cleanup;
}
D_DebugLog((DEB_TRACE_CTXT, "Client wants Datagram style\n"));
ContextFlags |= ISC_RET_DATAGRAM;
}
if ((ContextRequirements & ISC_REQ_USE_SESSION_KEY) != 0)
{
D_DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext Client wants sub-session key\n"));
//
// Can't do this with datagram because we need to be able to
// start sealing messages after the first call to Initialize.
//
// With a null session there is no real ticket so we don't ever
// need the server TGT either.
//
// Can't do DCE style because they don't have this.
//
if (!UseNullSession && (ContextRequirements & (ISC_REQ_DATAGRAM | ISC_REQ_USE_DCE_STYLE)) == 0)
{
//
// If we are in the first call, get a server TGT
//
if (ContextState == InvalidState)
{
GetServerTgt = TRUE;
}
ContextFlags |= ISC_RET_USE_SESSION_KEY;
}
else
{
D_DebugLog((DEB_WARN,"Client wanted both datagram and session key, dropping session key\n"));
}
}
if ((ContextRequirements & ISC_REQ_DELEGATE) != 0)
{
D_DebugLog((DEB_TRACE_CTXT, "Client wants Delegation\n"));
if ((ContextFlags & ISC_RET_MUTUAL_AUTH) == 0)
{
D_DebugLog((DEB_WARN,"Can't do delegate without mutual\n"));
}
else
{
ContextFlags |= ISC_RET_DELEGATE;
}
}
else if ((ContextRequirements & ISC_REQ_DELEGATE_IF_SAFE) != 0)
{
D_DebugLog((DEB_TRACE_CTXT, "Client wants Delegation, if safe\n"));
if ((ContextFlags & ISC_RET_MUTUAL_AUTH) == 0)
{
D_DebugLog((DEB_WARN,"Can't do delegate without mutual\n"));
}
else
{
ContextFlags |= ISC_RET_DELEGATE_IF_SAFE;
}
}
if ((ContextRequirements & ISC_REQ_CONNECTION) != 0)
{
D_DebugLog((DEB_TRACE_CTXT, "Client wants Connection style\n"));
ContextFlags |= ISC_RET_CONNECTION;
}
if ((ContextRequirements & ISC_REQ_IDENTIFY) != 0)
{
D_DebugLog((DEB_TRACE_CTXT, "Client wants Identify level\n"));
ContextFlags |= ISC_RET_IDENTIFY;
if (((ContextRequirements & ISC_REQ_DELEGATE) != 0) ||
((ContextRequirements & ISC_REQ_DELEGATE_IF_SAFE) != 0))
{
D_DebugLog((DEB_WARN, "Client wants Delegation and Indentify, turning off delegation\n"));
ContextFlags &= ~ISC_RET_DELEGATE;
ContextFlags &= ~ISC_RET_DELEGATE_IF_SAFE;
}
}
//////////////////////////////////////////////////////////////////////
//
// Get the ticket necessary to process the request. At this point:
// - TicketCacheEntry should contain the ticket to re-use
// - ErrorMessage should contain the error message, if there was one
//
/////////////////////////////////////////////////////////////////////
//
// Get the outbound ticket. If the credential has attached supplied
// credentials, get an AS ticket instead.
//
if (GetServerTgt)
{
//
// Nothing to do
//
}
else if (!UseNullSession)
{
D_DebugLog((DEB_TRACE_CTXT,"SpInitLsaModeContext: Getting outbound ticket for %wZ (%wZ) or ",
TargetName, &LocalTargetName ));
D_KerbPrintKdcName(DEB_TRACE_CTXT, TargetInternalName );
//
// If we got a skew error and we already have a cached ticket, don't
// bother getting a new ticket.
//
KerbReadLockContexts();
if (ErrorMessage != NULL)
{
if (((KERBERR) ErrorMessage->error_code == KRB_AP_ERR_SKEW) &&
(Context->TicketCacheEntry != NULL))
{
KerbReferenceTicketCacheEntry(Context->TicketCacheEntry);
TicketCacheEntry = Context->TicketCacheEntry;
}
else
{
//
// use2user assumes ticketTicketCacheEntry to be non null at
// this point
//
DsysAssert((Context->TicketCacheEntry != NULL) || ((ContextAttribs & KERB_CONTEXT_USER_TO_USER) == 0));
}
}
KerbUnlockContexts();
//
// If we don't have a ticket in the context, go ahead and get
// a new ticket
//
if (TicketCacheEntry == NULL)
{
D_DebugLog((DEB_TRACE, "Getting service ticket\n"));
Status = KerbGetServiceTicket(
LogonSession,
Credential,
CredManCredentials,
TargetInternalName,
&TargetDomainName,
SpnCacheEntry,
ProcessFlags,
TicketOptions,
0, // no enc type
ErrorMessage,
NULL, // no authorization data
TgtReply, // no tgt reply
&TicketCacheEntry,
NULL // don't return logon guid
);
if (Status == STATUS_USER2USER_REQUIRED)
{
D_DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext failed to get serviceticket: STATUS_USER2USER_REQUIRED\n"));
Status = STATUS_SUCCESS;
ContextFlags |= ISC_RET_USE_SESSION_KEY;
GetServerTgt = TRUE;
}
else if (!NT_SUCCESS(Status))
{
DebugLog((DEB_WARN,"Failed to get outbound ticket: 0x%x\n",Status));
goto Cleanup;
}
}
//
// fail user2user in data gram: not enough round trips to complete the protocol
//
if ((ContextFlags & ISC_RET_USE_SESSION_KEY) && (ContextFlags & ISC_RET_DATAGRAM))
{
DebugLog((DEB_ERROR, "SpInitLsaModeContext Client needed session key in datagram, dropping session key\n"));
ContextFlags |= ~ISC_RET_USE_SESSION_KEY;
Status = STATUS_LOGON_FAILURE;
goto Cleanup;
}
}
else
{
//
// Turn off all unsupported flags
//
ContextFlags &= ( ISC_RET_ALLOCATED_MEMORY |
ISC_RET_CONNECTION |
ISC_RET_DATAGRAM |
ISC_RET_NULL_SESSION );
}
//////////////////////////////////////////////////////////////////////
//
// Build the request - an AP request for standard Kerberos, or a TGT
// request.
//
/////////////////////////////////////////////////////////////////////
//
// For datagram requests, there is no output.
//
if ((ContextFlags & ISC_RET_DATAGRAM) == 0)
{
//
// This is the case where we are constructing a response.
//
if (OutputToken == NULL)
{
D_DebugLog((DEB_ERROR,"Trying to initialize a context with no output token! %ws, line %d\n", THIS_FILE, __LINE__));
Status = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
if (UseNullSession)
{
Status = KerbBuildNullSessionApRequest(
&Request,
&RequestSize
);
}
else if (GetServerTgt)
{
D_DebugLog((DEB_TRACE,"Building TGT request for "));
KerbPrintKdcName(DEB_TRACE, TargetInternalName);
if (((ContextRequirements & ISC_REQ_MUTUAL_AUTH) != 0) &&
(!ARGUMENT_PRESENT(TargetName) || TargetName->Length == 0))
{
D_DebugLog((DEB_ERROR, "Client wanted mutual auth, but did not supply target name\n"));
Status = SEC_E_UNSUPPORTED_FUNCTION;
goto Cleanup;
}
Status = KerbBuildTgtRequest(
TargetInternalName,
&TargetDomainName,
&ContextAttribs,
&Request,
&RequestSize
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
}
else
{
D_DebugLog((DEB_TRACE_CTXT2,"Building AP request for connection oriented context\n"));
Status = KerbBuildApRequest(
LogonSession,
Credential,
CredManCredentials,
TicketCacheEntry,
ErrorMessage,
ContextAttribs,
&ContextFlags,
&Request,
&RequestSize,
&Nonce,
&SubSessionKey,
pChannelBindings
);
//
// Set the receive nonce to be the nonce, as the code below
// expects it to be valid.
//
ReceiveNonce = Nonce;
}
if (!NT_SUCCESS(Status))
{
D_DebugLog((DEB_ERROR,"Failed to build AP request: 0x%x\n. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
if (OutputToken == NULL)
{
Status = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Return the AP request in the output buffer.
//
if ((ContextRequirements & ISC_REQ_ALLOCATE_MEMORY) == 0)
{
if (OutputToken->cbBuffer < RequestSize)
{
ULONG ErrorData[3];
ErrorData[0] = RequestSize;
ErrorData[1] = OutputToken->cbBuffer;
ErrorData[2] = ClientProcess;
D_DebugLog((DEB_ERROR,"Output token is too small - sent in %d, needed %d. %ws, line %d\n",
OutputToken->cbBuffer,RequestSize, THIS_FILE, __LINE__ ));
OutputToken->cbBuffer = RequestSize;
Status = STATUS_BUFFER_TOO_SMALL;
KerbReportNtstatus(
KERBEVT_INSUFFICIENT_TOKEN_SIZE,
Status,
NULL,
0,
ErrorData,
3
);
goto Cleanup;
}
RtlCopyMemory(
OutputToken->pvBuffer,
Request,
RequestSize
);
}
else
{
OutputToken->pvBuffer = Request;
if (OutputToken->pvBuffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
*ContextAttributes |= ISC_RET_ALLOCATED_MEMORY;
//
// Set this to NULL so it won't be freed by us on cleanup.
//
Request = NULL;
}
OutputToken->cbBuffer = RequestSize;
}
else
{
//
// All we do here is allocate a nonce for use in the context.
//
Nonce = KerbAllocateNonce();
if (OutputToken != NULL)
{
OutputToken->cbBuffer = 0;
}
}
//////////////////////////////////////////////////////////////////////
//
// If we haven't yet created a context, created one now. If we have,
// update the context with the latest status.
//
/////////////////////////////////////////////////////////////////////
//
// Allocate a client context, if we don't already have one
//
if (Context == NULL)
{
Status = KerbCreateClientContext(
LogonSession,
Credential,
CredManCredentials,
TicketCacheEntry,
TargetName,
Nonce,
ContextFlags,
ContextAttribs,
&SubSessionKey,
&Context,
&ContextLifetime
);
//CredManCredentials = NULL;
}
else
{
Status = KerbUpdateClientContext(
Context,
TicketCacheEntry,
Nonce,
ReceiveNonce,
ContextFlags,
ContextAttribs,
&SubSessionKey,
&ContextLifetime
);
}
if (!NT_SUCCESS(Status))
{
D_DebugLog((DEB_ERROR,"Failed to create client context: 0x%x. %ws, line %d\n",
Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// Keep track of network service session keys to detect whether network
// logon session is for local network service
//
if (RtlEqualLuid(&LogonId, &NetworkServiceLuid))
{
FILETIME CurTime = {0};
GetSystemTimeAsFileTime(&CurTime);
//
// use 2 times KerbGlobalSkewTime as ticket life time
//
KerbGetTime(*((TimeStamp*) &CurTime)) += 2 * KerbGetTime(KerbGlobalSkewTime);
Status = KerbCreateSKeyEntry(&SubSessionKey, &CurTime);
if (!NT_SUCCESS(Status))
{
D_DebugLog((DEB_ERROR, "Failed to create session key entry: 0x%x. %ws, line %d\n",
Status, THIS_FILE, __LINE__));
goto Cleanup;
}
}
//
// Hold on to the ticket for later use
//
KerbWriteLockContexts();
if ((Context->TicketCacheEntry == NULL) && (TicketCacheEntry != NULL))
{
KerbReferenceTicketCacheEntry(TicketCacheEntry);
Context->TicketCacheEntry = TicketCacheEntry;
}
ClientProcess = Context->ClientProcess;
KerbUnlockContexts();
//
// update the context with the marshalled target info.
//
if( NT_SUCCESS(Status) && pbMarshalledTargetInfo )
{
if( Context->pbMarshalledTargetInfo == NULL )
{
Context->pbMarshalledTargetInfo = pbMarshalledTargetInfo;
Context->cbMarshalledTargetInfo = cbMarshalledTargetInfo;
pbMarshalledTargetInfo = NULL;
}
}
//
// Return the correct flags
//
*NewContextHandle = KerbGetContextHandle(Context);
*ContextAttributes |= ContextFlags;
KerbUtcTimeToLocalTime(
ExpirationTime,
&ContextLifetime
);
//
// If mutual authentication was requested, ask for a continuation
//
if (((ContextFlags & ( ISC_RET_USED_DCE_STYLE |
ISC_RET_DATAGRAM |
ISC_RET_MUTUAL_AUTH )) != 0) ||
GetServerTgt )
{
Status = SEC_I_CONTINUE_NEEDED;
}
Cleanup:
// Adjust for the new meaning of delegate/delegate-if-safe if they got munged somehow.
if (ClientAskedForDelegateIfSafe && (*ContextAttributes & ISC_RET_DELEGATE))
{
(*ContextAttributes) &= ~ISC_RET_DELEGATE;
(*ContextAttributes) |= ISC_RET_DELEGATE_IF_SAFE;
}
else if ((ClientAskedForDelegate) && (*ContextAttributes & ISC_RET_DELEGATE_IF_SAFE))
{
(*ContextAttributes) &= ~ISC_RET_DELEGATE_IF_SAFE;
(*ContextAttributes) |= ISC_RET_DELEGATE;
}
if ( Status == STATUS_WRONG_PASSWORD )
{
//
// don't leak WRONG_PASSWORD to the caller.
//
Status = STATUS_LOGON_FAILURE;
}
if ( KerbEventTraceFlag ) // Event Trace: KerbInitSecurityContextEnd {Status, CredSource, DomainName, UserName, Target, (ExtErr), (Klininfo)}
{
PCWSTR TraceStrings[] =
{
L"CredMan",
L"Supplied",
L"Context",
L"LogonSession",
L"None"
};
enum { TSTR_CREDMAN = 0, TSTR_SUPPLIED, TSTR_CONTEXT, TSTR_LOGONSESSION, TSTR_NONE };
UNICODE_STRING UNICODE_NONE = { 4*sizeof(WCHAR), 4*sizeof(WCHAR), L"NONE" };
UNICODE_STRING CredSource;
PUNICODE_STRING trace_DomainName, trace_UserName, trace_target;
trace_target = (Context != NULL) ? &Context->ServerPrincipalName : &UNICODE_NONE;
if( Context != NULL && Context->CredManCredentials != NULL )
{
RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_CREDMAN] );
trace_DomainName = &Context->CredManCredentials->SuppliedCredentials->DomainName;
trace_UserName = &Context->CredManCredentials->SuppliedCredentials->UserName;
}
else if( Credential != NULL && Credential->SuppliedCredentials != NULL )
{
RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_SUPPLIED] );
trace_DomainName = &Credential->SuppliedCredentials->DomainName;
trace_UserName = &Credential->SuppliedCredentials->UserName;
}
else if( Context != NULL )
{
RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_CONTEXT] );
trace_DomainName = &Context->ClientRealm;
trace_UserName = &Context->ClientName;
}
else if( LogonSession != NULL )
{
RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_LOGONSESSION] );
trace_DomainName = &LogonSession->PrimaryCredentials.DomainName;
trace_UserName = &LogonSession->PrimaryCredentials.UserName;
}
else
{
RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_NONE] );
trace_DomainName = &UNICODE_NONE;
trace_UserName = &UNICODE_NONE;
}
INSERT_ULONG_INTO_MOF( Status, InitSCTraceInfo.MofData, 0 );
INSERT_UNICODE_STRING_INTO_MOF( CredSource, InitSCTraceInfo.MofData, 1 );
INSERT_UNICODE_STRING_INTO_MOF( *trace_DomainName, InitSCTraceInfo.MofData, 3 );
INSERT_UNICODE_STRING_INTO_MOF( *trace_UserName, InitSCTraceInfo.MofData, 5 );
INSERT_UNICODE_STRING_INTO_MOF( *trace_target, InitSCTraceInfo.MofData, 7 );
InitSCTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER) + 9*sizeof(MOF_FIELD);
//Check for extended error
if( pExtendedError != NULL )
{
INSERT_ULONG_INTO_MOF( pExtendedError->status, InitSCTraceInfo.MofData, 9 );
INSERT_ULONG_INTO_MOF( pExtendedError->klininfo, InitSCTraceInfo.MofData, 10 );
InitSCTraceInfo.EventTrace.Size += 2*sizeof(MOF_FIELD);
}
// Set trace parameters
InitSCTraceInfo.EventTrace.Guid = KerbInitSCGuid;
InitSCTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_END;
InitSCTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
TraceEvent(
KerbTraceLoggerHandle,
(PEVENT_TRACE_HEADER)&InitSCTraceInfo
);
}
//
// If we allocated a context, unlink it now
//
if (Context != NULL)
{
if (!NT_SUCCESS(Status))
{
//
// Only unlink the context if we just created it
//
if (ContextHandle == 0)
{
KerbReferenceContextByPointer(
Context,
TRUE
);
KerbDereferenceContext(Context);
}
else
{
//
// Set the context to an invalid state.
//
KerbWriteLockContexts();
Context->ContextState = InvalidState;
KerbUnlockContexts();
}
}
else
{
KerbWriteLockContexts();
if (Status == STATUS_SUCCESS)
{
Context->ContextState = AuthenticatedState;
}
else if (!GetServerTgt)
{
Context->ContextState = ApRequestSentState;
}
else
{
Context->ContextState = TgtRequestSentState;
//
// mark the context as user2user
//
Context->ContextAttributes |= KERB_CONTEXT_USER_TO_USER;
DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext (TGT in TGT reply) USER2USER-OUTBOUND set\n"));
}
KerbUnlockContexts();
}
KerbDereferenceContext(Context);
}
if ((Status == STATUS_SUCCESS) ||
((Status == SEC_I_CONTINUE_NEEDED) && ((ContextFlags & ISC_RET_DATAGRAM) != 0)))
{
NTSTATUS TempStatus;
//
// On real success we map the context to the callers address
// space.
//
TempStatus = KerbMapContext(
Context,
MappedContext,
ContextData
);
D_DebugLog((DEB_TRACE, "SpInitLsaModeContext called KerbMapContext ContextAttributes %#x, %#x\n", Context->ContextAttributes, TempStatus));
if (!NT_SUCCESS(TempStatus))
{
Status = TempStatus;
}
//
// Update the skew time with a success
//
KerbUpdateSkewTime(FALSE);
}
if (NULL != CredManCredentials)
{
KerbDereferenceCredmanCred(
CredManCredentials,
&LogonSession->CredmanCredentials
);
}
if( pbMarshalledTargetInfo )
{
LocalFree( pbMarshalledTargetInfo );
}
if (TgtReply != NULL)
{
KerbFreeData(KERB_TGT_REPLY_PDU, TgtReply);
}
if (LogonSession != NULL)
{
KerbDereferenceLogonSession( LogonSession );
}
if (Credential != NULL)
{
KerbDereferenceCredential( Credential );
}
if (TicketCacheEntry != NULL)
{
KerbDereferenceTicketCacheEntry( TicketCacheEntry );
}
if ( SpnCacheEntry != NULL )
{
KerbDereferenceSpnCacheEntry( SpnCacheEntry);
}
KerbFreeKerbError( ErrorMessage );
if (NULL != pExtendedError)
{
KerbFree(pExtendedError);
}
if (ErrorData != NULL)
{
MIDL_user_free(ErrorData);
}
if (Request != NULL)
{
KerbFree(Request);
}
if (Reply != NULL)
{
KerbFree(Reply);
}
KerbFreeKey(&SubSessionKey);
KerbFreeString( &TargetDomainName );
KerbFreeKdcName( &TargetInternalName );
D_DebugLog((DEB_TRACE_LEAKS,"SpInitLsaModeContext returned 0x%x, Context 0x%x, Pid 0x%x\n",KerbMapKerbNtStatusToNtStatus(Status), *NewContextHandle, ClientProcess));
D_DebugLog((DEB_TRACE_API, "SpInitLsaModeContext returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status)));
return(KerbMapKerbNtStatusToNtStatus(Status));
}
NTSTATUS NTAPI
SpApplyControlToken(
IN LSA_SEC_HANDLE ContextHandle,
IN PSecBufferDesc ControlToken
)
{
NTSTATUS Status = STATUS_NOT_SUPPORTED;
D_DebugLog((DEB_TRACE_API,"SpApplyControlToken Called\n"));
D_DebugLog((DEB_TRACE_API,"SpApplyControlToken returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status)));
return(KerbMapKerbNtStatusToNtStatus(Status));
}
#ifndef WIN32_CHICAGO //we don't do server side stuff
//+-------------------------------------------------------------------------
//
// Function: SpAcceptLsaModeContext
//
// Synopsis: Kerberos support routine for AcceptSecurityContext call.
// This routine accepts an AP request message from a client
// and verifies that it is a valid ticket. If mutual
// authentication is desired an AP reply is generated to
// send back to the client.
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS NTAPI
SpAcceptLsaModeContext(
IN OPTIONAL LSA_SEC_HANDLE CredentialHandle,
IN OPTIONAL LSA_SEC_HANDLE ContextHandle,
IN PSecBufferDesc InputBuffers,
IN ULONG ContextRequirements,
IN ULONG TargetDataRep,
OUT PLSA_SEC_HANDLE NewContextHandle,
OUT PSecBufferDesc OutputBuffers,
OUT PULONG ContextAttributes,
OUT PTimeStamp ExpirationTime,
OUT PBOOLEAN MappedContext,
OUT PSecBuffer ContextData
)
{
PKERB_LOGON_SESSION LogonSession = NULL;
PKERB_CREDENTIAL Credential = NULL;
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS LastStatus = STATUS_SUCCESS;
PKERB_AP_REQUEST Request = NULL;
PUCHAR Reply = NULL;
PSecBuffer InputToken = NULL;
PSecBuffer OutputToken = NULL;
ULONG Index;
ULONG ReplySize;
LUID LogonId;
PKERB_ENCRYPTED_TICKET InternalTicket = NULL;
PKERB_AUTHENTICATOR InternalAuthenticator = NULL;
KERB_ENCRYPTION_KEY SessionKey;
KERB_ENCRYPTION_KEY TicketKey;
KERB_ENCRYPTION_KEY ServerKey;
PKERB_CONTEXT Context = NULL;
TimeStamp ContextLifetime;
HANDLE TokenHandle = 0;
ULONG ContextFlags = 0;
ULONG ContextAttribs = KERB_CONTEXT_INBOUND;
ULONG Nonce = 0;
ULONG ReceiveNonce = 0;
BOOLEAN UseSuppliedCreds = FALSE;
ULONG_PTR LocalCredentialHandle = 0;
PSID UserSid = NULL;
KERBERR KerbErr = KDC_ERR_NONE;
KERB_CONTEXT_STATE ContextState = InvalidState;
UNICODE_STRING ServiceDomain = {0};
UNICODE_STRING ClientName = {0};
UNICODE_STRING ClientDomain = {0};
BOOLEAN IsTgtRequest = FALSE;
ULONG ClientProcess = 0;
PSEC_CHANNEL_BINDINGS pChannelBindings = NULL;
KERB_ACCEPTSC_INFO AcceptSCTraceInfo;
D_DebugLog((DEB_TRACE_API,"SpAcceptLsaModeContext 0x%x called\n",ContextHandle));
if( KerbEventTraceFlag ) // Event Trace: KerbAcceptSecurityContextStart {No Data}
{
AcceptSCTraceInfo.EventTrace.Guid = KerbAcceptSCGuid;
AcceptSCTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_START;
AcceptSCTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID;
AcceptSCTraceInfo.EventTrace.Size = sizeof (EVENT_TRACE_HEADER);
TraceEvent(
KerbTraceLoggerHandle,
(PEVENT_TRACE_HEADER)&AcceptSCTraceInfo
);
}
//
// Initialize the outputs.
//
*ContextAttributes = 0;
*NewContextHandle = 0;
*ExpirationTime = KerbGlobalHasNeverTime;
*MappedContext = FALSE;
ContextData->pvBuffer = NULL;
ContextData->cbBuffer = 0;
RtlZeroMemory(
&SessionKey,
sizeof(KERB_ENCRYPTION_KEY)
);
TicketKey = SessionKey;
ServerKey = TicketKey;
if (!KerbGlobalInitialized)
{
Status = STATUS_INVALID_SERVER_STATE;
goto Cleanup;
}
//
// First locate the Input token.
//
for (Index = 0; Index < InputBuffers->cBuffers ; Index++ )
{
if (BUFFERTYPE(InputBuffers->pBuffers[Index]) == SECBUFFER_TOKEN)
{
InputToken = &InputBuffers->pBuffers[Index];
Status = LsaFunctions->MapBuffer(InputToken,InputToken);
break;
}
}
if (!NT_SUCCESS(Status))
{
D_DebugLog((DEB_ERROR,"Failed to map Input token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// Check to see if we were passed channel bindings
//
for( Index = 0; Index < InputBuffers->cBuffers; Index++ )
{
if( BUFFERTYPE(InputBuffers->pBuffers[Index]) == SECBUFFER_CHANNEL_BINDINGS )
{
PVOID temp = NULL;
Status = LsaFunctions->MapBuffer(
&InputBuffers->pBuffers[Index],
&InputBuffers->pBuffers[Index]
);
if( !NT_SUCCESS(Status) )
{
goto Cleanup;
}
pChannelBindings = (PSEC_CHANNEL_BINDINGS) InputBuffers->pBuffers[Index].pvBuffer;
Status = KerbValidateChannelBindings(pChannelBindings,
InputBuffers->pBuffers[Index].cbBuffer);
if (!NT_SUCCESS(Status))
{
pChannelBindings = NULL;
goto Cleanup;
}
break;
}
}
//
// Locate the output token
//
for (Index = 0; Index < OutputBuffers->cBuffers ; Index++ )
{
if (BUFFERTYPE(OutputBuffers->pBuffers[Index]) == SECBUFFER_TOKEN)
{
OutputToken = &OutputBuffers->pBuffers[Index];
Status = LsaFunctions->MapBuffer(OutputToken,OutputToken);
break;
}
}
if (!NT_SUCCESS(Status))
{
D_DebugLog((DEB_ERROR,"Failed to map output token: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// If the context handle is no NULL we are finalizing a context
//
if (ContextHandle != 0)
{
if (InputToken == NULL)
{
D_DebugLog((DEB_ERROR,"Trying to complete a context with no input token! %ws, line %d\n", THIS_FILE, __LINE__));
Status = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// First reference the context.
//
Status = KerbReferenceContext(
ContextHandle,
FALSE, // don't unlink
&Context
);
if (Context == NULL)
{
D_DebugLog((DEB_ERROR,"Failed to reference context 0x%x. %ws, line %d\n",ContextHandle, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// Check the mode of the context to make sure we can finalize it.
//
KerbReadLockContexts();
ContextState = Context->ContextState;
if (((ContextState != ApReplySentState) &&
(ContextState != TgtReplySentState) &&
(ContextState != ErrorMessageSentState)) ||
((Context->ContextAttributes & KERB_CONTEXT_INBOUND) == 0))
{
D_DebugLog((DEB_ERROR,"Invalid context state: %d. %ws, line %d\n",
Context->ContextState, THIS_FILE, __LINE__));
Status = STATUS_INVALID_HANDLE;
KerbUnlockContexts();
goto Cleanup;
}
if ((Context->ContextAttributes & KERB_CONTEXT_USED_SUPPLIED_CREDS) != 0)
{
UseSuppliedCreds = TRUE;
}
ContextFlags = Context->ContextFlags;
LogonId = Context->LogonId;
LocalCredentialHandle = Context->CredentialHandle;
ClientProcess = Context->ClientProcess;
KerbUnlockContexts();
}
if (CredentialHandle != 0)
{
if ((LocalCredentialHandle != 0) && (CredentialHandle != LocalCredentialHandle))
{
D_DebugLog((DEB_ERROR,"Different cred handle passsed to subsequent call to AcceptSecurityContext: 0x%x instead of 0x%x. %ws, line %d\n",
CredentialHandle, LocalCredentialHandle, THIS_FILE, __LINE__ ));
Status = STATUS_WRONG_CREDENTIAL_HANDLE;
goto Cleanup;
}
}
else
{
CredentialHandle = LocalCredentialHandle;
}
//
// If we are finalizing a context, do that here
//
if (ContextState == ApReplySentState)
{
//
// If we are doing datgram, then the finalize is actually an AP request
//
if ((ContextFlags & ISC_RET_DATAGRAM) != 0)
{
//
// Get the logon session
//
LogonSession = KerbReferenceLogonSession( &LogonId, FALSE );
if (LogonSession == NULL)
{
Status = STATUS_NO_SUCH_LOGON_SESSION;
goto Cleanup;
}
//
// If we are using supplied creds, get the credentials. Copy
// out the domain name so we can use it to verify the PAC.
//
Status = KerbReferenceCredential(
LocalCredentialHandle,
KERB_CRED_INBOUND,
FALSE, // don't dereference
&Credential
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
if (UseSuppliedCreds)
{
KerbReadLockLogonSessions(LogonSession);
Status = KerbDuplicateString(
&ServiceDomain,
&Credential->SuppliedCredentials->DomainName
);
KerbUnlockLogonSessions(LogonSession);
}
else
{
KerbReadLockLogonSessions(LogonSession);
Status = KerbDuplicateString(
&ServiceDomain,
&LogonSession->PrimaryCredentials.DomainName
);
KerbUnlockLogonSessions(LogonSession);
}
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Verify the AP request
//
Status = KerbVerifyApRequest(
Context,
(PUCHAR) InputToken->pvBuffer,
InputToken->cbBuffer,
LogonSession,
Credential,
UseSuppliedCreds,
((ContextRequirements & ASC_REQ_ALLOW_CONTEXT_REPLAY) == 0),
&Request,
&InternalTicket,
&InternalAuthenticator,
&SessionKey,
&TicketKey,
&ServerKey,
&ContextFlags,
&ContextAttribs,
&KerbErr,
pChannelBindings
);
//
// We don't allow user-to-user recovery with datagram
//
if ((Status == STATUS_REPARSE_OBJECT) // this is a TGT request
|| ((Status == SEC_E_NO_CREDENTIALS) && (KerbErr == KRB_AP_ERR_USER_TO_USER_REQUIRED)))
{
DebugLog((DEB_ERROR, "Won't allow user2user with Datagram. %ws, line %d\n", THIS_FILE, __LINE__));
Status = SEC_E_INVALID_TOKEN;
KerbErr = KRB_AP_ERR_MSG_TYPE;
goto ErrorReturn;
}
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to verify AP request: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
//
// Let the skew tracker know about the failure
//
if (KerbErr == KRB_AP_ERR_SKEW)
{
KerbUpdateSkewTime(TRUE);
}
//
// Go to ErrorReturn so we can return an error message
//
goto ErrorReturn;
}
//
// Turn on the flag if it was called for
//
if ((ContextRequirements & ASC_REQ_ALLOW_CONTEXT_REPLAY) != 0)
{
ContextFlags |= ASC_RET_ALLOW_CONTEXT_REPLAY;
}
//
// Record the success
//
KerbUpdateSkewTime(FALSE);
//
// Check if the caller wants to allow null sessions
//
if (((ContextFlags & ISC_RET_NULL_SESSION) != 0) &&
((ContextRequirements & ASC_REQ_ALLOW_NULL_SESSION) == 0))
{
D_DebugLog((DEB_ERROR,"Received null session but not allowed. %ws, line %d\n", THIS_FILE, __LINE__));
Status = STATUS_LOGON_FAILURE;
goto Cleanup;
}
//
// Save away the ReceiveNonce if it was provided
//
if ((InternalAuthenticator!= NULL) &&
((InternalAuthenticator->bit_mask & KERB_AUTHENTICATOR_sequence_number_present) != 0))
{
//
// If the number is unsigned, convert it as unsigned. Otherwise
// convert as signed.
//
if (ASN1intxisuint32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number))
{
ReceiveNonce = ASN1intx2uint32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number);
}
else
{
ReceiveNonce = (ULONG) ASN1intx2int32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number);
}
}
else
{
ReceiveNonce = 0;
}
//
// Authentication succeeded, so build a token
//
Status = KerbCreateTokenFromTicket(
InternalTicket,
InternalAuthenticator,
ContextFlags,
&ServerKey,
&ServiceDomain,
&SessionKey,
&LogonId,
&UserSid,
&TokenHandle,
&ClientName,
&ClientDomain
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to create token from ticket: 0x%x. %ws, line %d\n",
Status, THIS_FILE, __LINE__));
goto Cleanup;
}
Status = KerbUpdateServerContext(
Context,
InternalTicket,
Request,
&SessionKey,
&LogonId,
&UserSid,
ContextFlags,
ContextAttribs,
Nonce,
ReceiveNonce,
&TokenHandle,
&ClientName,
&ClientDomain,
&ContextLifetime
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to create server context: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
}
else
{
//
// Now unpack the AP reply
//
Status = KerbVerifyApReply(
Context,
(PUCHAR) InputToken->pvBuffer,
InputToken->cbBuffer,
&Nonce
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to verify AP reply: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// We're done - we finalized.
//
Status = STATUS_SUCCESS;
if (OutputToken != NULL)
{
OutputToken->cbBuffer = 0;
}
}
KerbReadLockContexts();
*ContextAttributes = KerbMapContextFlags(Context->ContextFlags);
KerbUtcTimeToLocalTime(
ExpirationTime,
&Context->Lifetime
);
if (OutputToken != NULL)
{
OutputToken->cbBuffer = 0;
}
*NewContextHandle = ContextHandle;
KerbUnlockContexts();
goto Cleanup; // datagram and finalized contexts exit here.
}
//
// Get the associated credential
//
Status = KerbReferenceCredential(
CredentialHandle,
KERB_CRED_INBOUND,
FALSE,
&Credential
);
if (!NT_SUCCESS(Status))
{
D_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.
//
LogonId = Credential->LogonId;
//
// Get the logon session
//
LogonSession = KerbReferenceLogonSession( &LogonId, FALSE );
if (LogonSession == NULL)
{
Status = STATUS_NO_SUCH_LOGON_SESSION;
goto Cleanup;
}
KerbReadLockLogonSessions(LogonSession);
if ((Credential->CredentialFlags & KERB_CRED_LOCAL_ACCOUNT) != 0)
{
D_DebugLog((DEB_WARN, "Trying to use a local logon session with Kerberos\n"));
KerbUnlockLogonSessions(LogonSession);
Status = SEC_E_NO_CREDENTIALS;
goto Cleanup;
}
if (Credential->SuppliedCredentials != NULL)
{
UseSuppliedCreds = TRUE;
ContextAttribs |= KERB_CONTEXT_USED_SUPPLIED_CREDS;
Status = KerbDuplicateString(
&ServiceDomain,
&Credential->SuppliedCredentials->DomainName
);
}
else
{
Status = KerbDuplicateString(
&ServiceDomain,
&LogonSession->PrimaryCredentials.DomainName
);
}
#if DBG
D_DebugLog((DEB_TRACE_CTXT, "SpAcceptLsaModeContext: Accepting context for %wZ\\%wZ\n",
&LogonSession->PrimaryCredentials.DomainName,
&LogonSession->PrimaryCredentials.UserName ));
#endif
KerbUnlockLogonSessions(LogonSession);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// If datagram was requested, note it now. There is no input
// buffer on the first call using datagram.
//
if ((ContextRequirements & ASC_REQ_DATAGRAM) != 0)
{
D_DebugLog((DEB_TRACE_CTXT2, "Accepting datagram first call\n"));
//
// Verify that there is no input token or it is small. RPC passes
// in two bytes for the DEC package that we can ignore.
//
if ((InputToken != NULL) &&
(InputToken->cbBuffer > 4))
{
D_DebugLog((DEB_WARN, "Non null input token passed to AcceptSecurityContext for datagram\n"));
Status = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
ContextFlags |= ISC_RET_DATAGRAM;
ReceiveNonce = 0;
//
// Build a server context
//
Status = KerbCreateEmptyContext(
Credential,
ContextFlags,
ContextAttribs,
&LogonId,
&Context,
&ContextLifetime
);
if (!NT_SUCCESS(Status))
{
D_DebugLog((DEB_ERROR,"Failed to create server context: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
if (OutputToken != NULL)
{
OutputToken->cbBuffer = 0;
}
}
else
{
D_DebugLog((DEB_TRACE_CTXT2,"Accepting connection first call\n"));
//
// Unmarshall the AP request
//
if ((InputToken == NULL) ||
(InputToken->cbBuffer == 0))
{
D_DebugLog((DEB_WARN, "Null input token passed to AcceptSecurityContext for datagram\n"));
Status = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Verify the AP request
//
Status = KerbVerifyApRequest(
Context,
(PUCHAR) InputToken->pvBuffer,
InputToken->cbBuffer,
LogonSession,
Credential,
UseSuppliedCreds,
((ContextRequirements & ASC_REQ_ALLOW_CONTEXT_REPLAY) == 0),
&Request,
&InternalTicket,
&InternalAuthenticator,
&SessionKey,
&TicketKey,
&ServerKey,
&ContextFlags,
&ContextAttribs,
&KerbErr,
pChannelBindings
);
if (!NT_SUCCESS(Status))
{
//
// Track time skew errors
//
if ((KerbErr == KRB_AP_ERR_SKEW) ||
(KerbErr == KRB_AP_ERR_TKT_NYV))
{
KerbUpdateSkewTime(TRUE);
}
DebugLog((DEB_ERROR,"Failed to verify AP request: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto ErrorReturn;
}
ContextFlags |= ISC_RET_CONNECTION;
//
// Check if this was a user-to-user tgt request. If so, then
// there was no AP request
//
if (Status == STATUS_REPARSE_OBJECT)
{
IsTgtRequest = TRUE;
Status = KerbHandleTgtRequest(
LogonSession,
Credential,
UseSuppliedCreds,
(PUCHAR) InputToken->pvBuffer,
InputToken->cbBuffer,
ContextRequirements,
OutputToken,
&LogonId,
ContextAttributes,
&Context,
&ContextLifetime,
&KerbErr
);
if (!NT_SUCCESS(Status))
{
goto ErrorReturn;
}
ContextFlags |= ISC_RET_USE_SESSION_KEY;
D_DebugLog((DEB_TRACE_U2U, "SpAcceptLsaModeContext handled TGT request and use_session_key set, ContextAttributes %#x\n", Context->ContextAttributes));
}
else // not a user-to-user request
{
//
// Track successful time if this wasn't an error recovery
//
if (ContextState != ErrorMessageSentState)
{
KerbUpdateSkewTime(FALSE);
}
if ((InternalAuthenticator != NULL) &&
(InternalAuthenticator->bit_mask & KERB_AUTHENTICATOR_sequence_number_present))
{
//
// If the number is unsigned, convert it as unsigned. Otherwise
// convert as signed.
//
if (ASN1intxisuint32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number))
{
ReceiveNonce = ASN1intx2uint32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number);
}
else
{
ReceiveNonce = (ULONG) ASN1intx2int32(&InternalAuthenticator->KERB_AUTHENTICATOR_sequence_number);
}
}
else
{
ReceiveNonce = 0;
}
//
// Initialize the opposite direction nonce to the same value
//
Nonce = ReceiveNonce;
//
// Turn on the flag if it was called for
//
if ((ContextRequirements & ASC_REQ_ALLOW_CONTEXT_REPLAY) != 0)
{
ContextFlags |= ASC_RET_ALLOW_CONTEXT_REPLAY;
}
//
// Check if the caller wants to allow null sessions
//
if (((ContextFlags & ISC_RET_NULL_SESSION) != 0) &&
((ContextRequirements & ASC_REQ_ALLOW_NULL_SESSION) == 0))
{
D_DebugLog((DEB_ERROR,"Received null session but not allowed. %ws, line %d\n", THIS_FILE, __LINE__));
Status = STATUS_LOGON_FAILURE;
goto Cleanup;
}
//
// Authentication succeeded, so build a token
//
D_DebugLog((DEB_TRACE_CTXT2, "AcceptLsaModeContext: Creating token from ticket\n"));
Status = KerbCreateTokenFromTicket(
InternalTicket,
InternalAuthenticator,
ContextFlags,
&ServerKey,
&ServiceDomain,
&SessionKey,
&LogonId,
&UserSid,
&TokenHandle,
&ClientName,
&ClientDomain
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to create token from ticket: 0x%x. %ws, line %d\n",
Status, THIS_FILE, __LINE__));
goto Cleanup;
}
//
// If the caller wants mutual authentication, build an AP reply
//
if (((ContextFlags & ISC_RET_MUTUAL_AUTH) != 0) ||
((ContextFlags & ISC_RET_USED_DCE_STYLE) != 0))
{
//
// We require an output token in this case.
//
if (OutputToken == NULL)
{
Status = SEC_E_INVALID_TOKEN;
goto Cleanup;
}
//
// Build the reply message
//
D_DebugLog((DEB_TRACE_CTXT2,"SpAcceptLsaModeContext: Building AP reply\n"));
Status = KerbBuildApReply(
InternalAuthenticator,
Request,
ContextFlags,
ContextAttribs,
&TicketKey,
&SessionKey,
&Nonce,
&Reply,
&ReplySize
);
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to build AP reply: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
if (OutputToken == NULL)
{
D_DebugLog((DEB_ERROR,"Output token missing. %ws, line %d\n", THIS_FILE, __LINE__));
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
//
// Return the AP reply in the output buffer.
//
if ((ContextRequirements & ISC_REQ_ALLOCATE_MEMORY) == 0)
{
if (OutputToken->cbBuffer < ReplySize)
{
ULONG ErrorData[3];
ErrorData[0] = ReplySize;
ErrorData[1] = OutputToken->cbBuffer;
ErrorData[2] = ClientProcess;
D_DebugLog((DEB_ERROR,"Output token is too small - sent in %d, needed %d. %ws, line %d\n",
OutputToken->cbBuffer,ReplySize, THIS_FILE, __LINE__ ));
OutputToken->cbBuffer = ReplySize;
Status = STATUS_BUFFER_TOO_SMALL;
KerbReportNtstatus(
KERBEVT_INSUFFICIENT_TOKEN_SIZE,
Status,
NULL,
0,
ErrorData,
3
);
goto Cleanup;
}
RtlCopyMemory(
OutputToken->pvBuffer,
Reply,
ReplySize
);
}
else
{
OutputToken->pvBuffer = Reply;
Reply = NULL;
*ContextAttributes |= ASC_RET_ALLOCATED_MEMORY;
}
OutputToken->cbBuffer = ReplySize;
}
else
{
if (OutputToken != NULL)
{
OutputToken->cbBuffer = 0;
}
}
//
// Build a server context if we don't already have one.
//
//
// Turn on the flag if it was called for
//
if ((ContextRequirements & ASC_REQ_ALLOW_CONTEXT_REPLAY) != 0)
{
ContextFlags |= ASC_RET_ALLOW_CONTEXT_REPLAY;
}
if (Context == NULL)
{
Status = KerbCreateServerContext(
LogonSession,
Credential,
InternalTicket,
Request,
&SessionKey,
&LogonId,
&UserSid,
ContextFlags,
ContextAttribs,
Nonce,
ReceiveNonce,
&TokenHandle,
&ClientName,
&ClientDomain,
&Context,
&ContextLifetime
);
}
else
{
//
// Update an existing context
//
Status = KerbUpdateServerContext(
Context,
InternalTicket,
Request,
&SessionKey,
&LogonId,
&UserSid,
ContextFlags,
ContextAttribs,
Nonce,
ReceiveNonce,
&TokenHandle,
&ClientName,
&ClientDomain,
&ContextLifetime
);
}
if (!NT_SUCCESS(Status))
{
DebugLog((DEB_ERROR,"Failed to create or update server context: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
goto Cleanup;
}
} // not a TGT request
} // not datagram
*NewContextHandle = KerbGetContextHandle(Context);
KerbUtcTimeToLocalTime(
ExpirationTime,
&ContextLifetime
);
#if DBG
KerbReadLockContexts();
ClientProcess = Context->ClientProcess;
KerbUnlockContexts();
#endif // DBG
*ContextAttributes |= KerbMapContextFlags(ContextFlags);
if (IsTgtRequest || (((ContextFlags & ISC_RET_USED_DCE_STYLE) != 0) ||
((ContextFlags & ISC_RET_DATAGRAM) != 0)))
{
Status = SEC_I_CONTINUE_NEEDED;
}
goto Cleanup;
ErrorReturn:
//
// Generate a KERB_ERROR message if necessary, meaning that there was
// an authentication failure.
//
if ((OutputToken != NULL ) &&
(!KERB_SUCCESS(KerbErr) ||
((ContextRequirements & ASC_REQ_EXTENDED_ERROR) != 0) ||
((ContextFlags & ISC_RET_EXTENDED_ERROR) != 0)))
{
NTSTATUS TempStatus;
PBYTE ErrorMessage = NULL;
ULONG ErrorMessageSize;
PBYTE ErrorData = NULL;
ULONG ErrorDataSize = 0;
//
// Check whether it is an error we want the client to retry on.
// For datagram, we can't handle this.
//
if (ContextRequirements & ASC_REQ_DATAGRAM)
{
goto Cleanup;
}
if (!(((ContextRequirements & ASC_REQ_EXTENDED_ERROR) != 0) ||
(KerbErr == KRB_AP_ERR_USER_TO_USER_REQUIRED) ||
(KerbErr == KRB_AP_ERR_SKEW) ||
(KerbErr == KRB_AP_ERR_TKT_NYV) ||
(KerbErr == KRB_AP_ERR_TKT_EXPIRED) ||
(KerbErr == KRB_AP_ERR_MODIFIED) ))
{
goto Cleanup;
}
//
// Create an empty context that can be used later
//
if (Context == NULL)
{
TempStatus = KerbCreateEmptyContext(
Credential,
ContextFlags,
ContextAttribs,
&LogonId,
&Context,
&ContextLifetime
);
if (!NT_SUCCESS(TempStatus))
{
goto Cleanup;
}
}
//
// if the error code is one with error data, build the error data
//
switch ((UINT_PTR) KerbErr)
{
case (UINT_PTR) KRB_AP_ERR_USER_TO_USER_REQUIRED:
NTSTATUS TempStatus;
TempStatus = KerbBuildTgtErrorReply(
LogonSession,
Credential,
UseSuppliedCreds,
Context,
&ErrorDataSize,
&ErrorData
);
D_DebugLog((DEB_TRACE_U2U, "SpAcceptLsaModeContext called KerbBuildTgtErrorReply %#x\n", TempStatus));
if (TempStatus == STATUS_USER2USER_REQUIRED)
{
KerbErr = KRB_AP_ERR_NO_TGT;
}
else if (!NT_SUCCESS(TempStatus))
{
D_DebugLog((DEB_ERROR,"Failed to build tgt error reply: 0x%x. Ignoring. %ws, line %d\n",TempStatus, THIS_FILE, __LINE__));
}
break;
case (UINT_PTR) KDC_ERR_NONE:
//
// In this case, return the KRB_ERR_GENERIC and the NTSTATUS code
// in the error data
//
KerbErr = KRB_ERR_GENERIC;
ErrorData = (PUCHAR) &Status;
ErrorDataSize = sizeof(ULONG);
break;
}
TempStatus = KerbBuildGssErrorMessage(
KerbErr,
ErrorData,
ErrorDataSize,
Context,
&ErrorMessageSize,
&ErrorMessage
);
if ((ErrorData != NULL) && (ErrorData != (PUCHAR) &Status))
{
MIDL_user_free(ErrorData);
}
if (!NT_SUCCESS(TempStatus))
{
goto Cleanup;
}
*ContextAttributes |= ASC_RET_EXTENDED_ERROR;
if ((ContextRequirements & ISC_REQ_ALLOCATE_MEMORY) == 0)
{
if (OutputToken->cbBuffer < ErrorMessageSize)
{
D_DebugLog((DEB_ERROR,"Output token is too small - sent in %d, needed %d. %ws, line %d\n",
OutputToken->cbBuffer,ErrorMessageSize, THIS_FILE, __LINE__ ));
MIDL_user_free(ErrorMessage);
goto Cleanup;
}
else
{
DsysAssert(OutputToken->pvBuffer != NULL);
RtlCopyMemory(
OutputToken->pvBuffer,
ErrorMessage,
ErrorMessageSize
);
OutputToken->cbBuffer = ErrorMessageSize;
MIDL_user_free(ErrorMessage);
}
}
else
{
DsysAssert(OutputToken->pvBuffer == NULL);
OutputToken->cbBuffer = ErrorMessageSize;
OutputToken->pvBuffer = ErrorMessage;
ErrorMessage = NULL;
*ContextAttributes |= ASC_RET_ALLOCATED_MEMORY;
}
*ContextAttributes |= ASC_RET_EXTENDED_ERROR;
*NewContextHandle = KerbGetContextHandle(Context);
KerbUtcTimeToLocalTime(
ExpirationTime,
&ContextLifetime
);
*ContextAttributes |= KerbMapContextFlags(ContextFlags);
LastStatus = Status;
//
// now it is time to mark the context as user2user
//
if (KerbErr == KRB_AP_ERR_USER_TO_USER_REQUIRED)
{
DebugLog((DEB_TRACE_U2U, "SpInitLsaModeContext (TGT in error reply) USER2USER-INBOUND set\n"));
KerbWriteLockContexts()
Context->ContextAttributes |= KERB_CONTEXT_USER_TO_USER;
KerbUnlockContexts();
}
Status = SEC_I_CONTINUE_NEEDED;
}
Cleanup:
if( KerbEventTraceFlag ) // Event Trace: KerbAcceptSecurityContextEnd {Status, CredSource, DomainName, UserName, Target, (ExtError), (klininfo)}
{
PCWSTR TraceStrings[] =
{
L"CredMan",
L"Supplied",
L"Context",
L"LogonSession",
L"None"
};
enum { TSTR_CREDMAN = 0, TSTR_SUPPLIED, TSTR_CONTEXT, TSTR_LOGONSESSION, TSTR_NONE };
UNICODE_STRING UNICODE_NONE = { 4*sizeof(WCHAR), 4*sizeof(WCHAR), L"NONE" };
UNICODE_STRING CredSource;
PUNICODE_STRING trace_DomainName, trace_UserName, trace_target;
trace_target = (Context!=NULL) ? &Context->ServerPrincipalName : &UNICODE_NONE;
if( Context != NULL && Context->CredManCredentials != NULL )
{
RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_CREDMAN] );
trace_DomainName = &Context->CredManCredentials->SuppliedCredentials->DomainName;
trace_UserName = &Context->CredManCredentials->SuppliedCredentials->UserName;
}
else if( Credential != NULL && Credential->SuppliedCredentials != NULL )
{
RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_SUPPLIED] );
trace_DomainName = &Credential->SuppliedCredentials->DomainName;
trace_UserName = &Credential->SuppliedCredentials->UserName;
}
else if( Context != NULL )
{
RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_CONTEXT] );
trace_DomainName = &Context->ClientRealm;
trace_UserName = &Context->ClientName;
}
else if( LogonSession != NULL )
{
RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_LOGONSESSION] );
trace_DomainName = &LogonSession->PrimaryCredentials.DomainName;
trace_UserName = &LogonSession->PrimaryCredentials.UserName;
}
else
{
RtlInitUnicodeString( &CredSource, TraceStrings[TSTR_NONE] );
trace_DomainName = &UNICODE_NONE;
trace_UserName = &UNICODE_NONE;
}
INSERT_ULONG_INTO_MOF( Status, AcceptSCTraceInfo.MofData, 0 );
INSERT_UNICODE_STRING_INTO_MOF( CredSource, AcceptSCTraceInfo.MofData, 1 );
INSERT_UNICODE_STRING_INTO_MOF( *trace_DomainName, AcceptSCTraceInfo.MofData, 3 );
INSERT_UNICODE_STRING_INTO_MOF( *trace_UserName, AcceptSCTraceInfo.MofData, 5 );
INSERT_UNICODE_STRING_INTO_MOF( *trace_target, AcceptSCTraceInfo.MofData, 7 );
AcceptSCTraceInfo.EventTrace.Size = sizeof(EVENT_TRACE_HEADER) + 9*sizeof(MOF_FIELD);
// Set trace parameters
AcceptSCTraceInfo.EventTrace.Guid = KerbAcceptSCGuid;
AcceptSCTraceInfo.EventTrace.Class.Type = EVENT_TRACE_TYPE_END;
AcceptSCTraceInfo.EventTrace.Flags = WNODE_FLAG_TRACED_GUID | WNODE_FLAG_USE_MOF_PTR;
TraceEvent(
KerbTraceLoggerHandle,
(PEVENT_TRACE_HEADER)&AcceptSCTraceInfo
);
}
//
// First, handle auditing
//
if (Status == STATUS_SUCCESS)
{
//
// Don't audit if we didn't create a token.
//
if (Context->UserSid != NULL)
{
UNICODE_STRING WorkstationName = {0};
//
// note that UserSid will only be non-Null if the ClientName, ClientRealm were updated in the context.
// and the context is currently referenced, so the fields won't vanish under us.
//
if ((InternalTicket != NULL) &&
((InternalTicket->bit_mask & KERB_ENCRYPTED_TICKET_client_addresses_present) != 0))
{
(VOID) KerbGetClientNetbiosAddress(
&WorkstationName,
InternalTicket->KERB_ENCRYPTED_TICKET_client_addresses
);
//
// The following generates a successful audit event.
// A new field (logon GUID) was added to this audit event.
//
// In order to send this new field to LSA, we had two options:
// 1) add new function (AuditLogonEx) to LSA dispatch table
// 2) define a private (LsaI) function to do the job
//
// option#2 was chosen because the logon GUID is a Kerberos only
// feature.
//
(void) KerbAuditLogon(
Status,
Status,
InternalTicket,
Context->UserSid,
&WorkstationName,
&LogonId
);
KerbFreeString(&WorkstationName);
}
}
}
else if (!NT_SUCCESS(Status) || (LastStatus != STATUS_SUCCESS))
{
if (Context != NULL)
{
LsaFunctions->AuditLogon(
STATUS_LOGON_FAILURE,
(LastStatus != STATUS_SUCCESS) ? LastStatus : Status,
&Context->ClientName,
&Context->ClientRealm,
NULL, // no workstation
NULL, // no sid instead of a bogus one
Network,
&KerberosSource,
&LogonId
);
}
else
{
UNICODE_STRING EmptyString = NULL_UNICODE_STRING;
LsaFunctions->AuditLogon(
(LastStatus != STATUS_SUCCESS) ? LastStatus : Status,
STATUS_SUCCESS,
&EmptyString,
&EmptyString,
NULL, // no workstation
NULL,
Network,
&KerberosSource,
&LogonId
);
}
}
if (Context != NULL)
{
if (!NT_SUCCESS(Status))
{
//
// Only unlink the context if we just created it.
//
if (ContextHandle == 0)
{
KerbReferenceContextByPointer(
Context,
TRUE
);
KerbDereferenceContext(Context);
}
else
{
//
// Set the context to an invalid state.
//
KerbWriteLockContexts();
Context->ContextState = InvalidState;
KerbUnlockContexts();
}
}
else
{
KerbWriteLockContexts();
if (Status == STATUS_SUCCESS)
{
Context->ContextState = AuthenticatedState;
}
else
{
if ((*ContextAttributes & ASC_RET_EXTENDED_ERROR) != 0)
{
Context->ContextState = ErrorMessageSentState;
}
else if (!IsTgtRequest)
{
Context->ContextState = ApReplySentState;
}
else
{
//
// else the HandleTgtRequest set the state
//
DsysAssert(Context->ContextState == TgtReplySentState);
}
}
KerbUnlockContexts();
}
KerbDereferenceContext(Context);
}
if (Status == STATUS_SUCCESS)
{
//
// On real success we map the context to the callers address
// space.
//
Status = KerbMapContext(
Context,
MappedContext,
ContextData
);
DebugLog((DEB_TRACE, "SpAcceptLsaModeContext called KerbMapContext ContextAttributes %#x, %#x\n", Context->ContextAttributes, Status));
}
if (LogonSession != NULL)
{
KerbDereferenceLogonSession( LogonSession );
}
if (Credential != NULL)
{
KerbDereferenceCredential( Credential );
}
if (InternalTicket != NULL)
{
KerbFreeTicket( InternalTicket );
}
if (InternalAuthenticator != NULL)
{
KerbFreeAuthenticator(InternalAuthenticator);
}
if (Request != NULL)
{
KerbFreeApRequest(Request);
}
if (Reply != NULL)
{
KerbFree(Reply);
}
KerbFreeKey(&SessionKey);
KerbFreeKey(&ServerKey);
if (UserSid != NULL)
{
KerbFree(UserSid);
}
//
// If there was a problem with the context or AP reply, the TokenHandle
// will not be reset to NULL. If it is NULL, close it so we don't leak
//
if ( TokenHandle != NULL )
{
D_DebugLog(( DEB_TRACE, "Closing token handle because context creation failed (%x)\n",
Status ));
NtClose( TokenHandle );
}
//
// Update performance counter
//
#ifndef WIN32_CHICAGO
if (ContextHandle == 0)
{
I_SamIIncrementPerformanceCounter(KerbServerContextCounter);
}
#endif // WIN32_CHICAGO
KerbFreeKey(&TicketKey);
KerbFreeString(&ServiceDomain);
KerbFreeString(&ClientDomain);
KerbFreeString(&ClientName);
D_DebugLog((DEB_TRACE_LEAKS,"SpAcceptLsaModeContext returned 0x%x, Context 0x%x, Pid 0x%x\n",KerbMapKerbNtStatusToNtStatus(Status), *NewContextHandle, ClientProcess));
D_DebugLog((DEB_TRACE_API, "SpAcceptLsaModeContext returned 0x%x\n", KerbMapKerbNtStatusToNtStatus(Status)));
return(KerbMapKerbNtStatusToNtStatus(Status));
}
#endif WIN32_CHICAGO //we don't do server side stuff