mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4471 lines
136 KiB
4471 lines
136 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// 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
|