//+-----------------------------------------------------------------------
//
// 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