//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1996
//
// File:        logonses.cxx
//
// Contents:    Code for managing the global list of logon sessions
//
//
// History:     16-April-1996   Created         MikeSw
//
//------------------------------------------------------------------------

#include <kerb.hxx>
#define LOGONSES_ALLOCATE
#include <kerbp.h>


#ifdef DEBUG_SUPPORT
static TCHAR THIS_FILE[]=TEXT(__FILE__);
#endif


//+-------------------------------------------------------------------------
//
//  Function:   KerbInitLogonSessionList
//
//  Synopsis:   Initializes logon session list
//
//  Effects:    allocates a resources
//
//  Arguments:  none
//
//  Requires:
//
//  Returns:    STATUS_SUCCESS on success, other error codes
//              on failure
//
//  Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbInitLogonSessionList(
    VOID
    )
{
    NTSTATUS Status;


    Status = KerbInitializeList( &KerbLogonSessionList );
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }
    KerberosLogonSessionsInitialized = TRUE;

Cleanup:
    if (!NT_SUCCESS(Status))
    {
        KerbFreeList( &KerbLogonSessionList);

    }
    return(Status);
}

//+-------------------------------------------------------------------------
//
//
//  Function: KerbInitNetworkServiceLoopbackDetection
//
//  Synopsis: Initialize the network service session loopback detection
//
//  Effects: Allocates a resources
//
//  Arguments: none
//
//  Requires:
//
//  Returns: STATUS_SUCCESS on success, other error codes on failure
//
//  Notes:
//
//--------------------------------------------------------------------------

NTSTATUS
KerbInitNetworkServiceLoopbackDetection(
    VOID
    )
{
   NTSTATUS Status = STATUS_SUCCESS;

   InitializeListHead(&KerbNetworkServiceSKeyList);

#if DBG

   KerbcSKeyEntries = 0;

#endif

   KerbhSKeyTimerQueue = NULL;

   __try
   {
       RtlInitializeResource(&KerbNetworkServiceSKeyLock);
   }
   __except(EXCEPTION_EXECUTE_HANDLER)
   {
       Status = STATUS_INSUFFICIENT_RESOURCES;
   }

   return Status;
}

//+-------------------------------------------------------------------------
//
//  Function: KerbFreeNetworkServiceSKeyListAndLock
//
//  Synopsis: Free the network service session key list and its lock
//
//  Effects: Frees a resources
//
//  Arguments: none
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//--------------------------------------------------------------------------

VOID
KerbFreeNetworkServiceSKeyListAndLock(
    VOID
    )
{
   if (RtlAcquireResourceExclusive(&KerbNetworkServiceSKeyLock, TRUE))
   {
        for (LIST_ENTRY* pListEntry = KerbNetworkServiceSKeyList.Flink;
             pListEntry != &KerbNetworkServiceSKeyList;
             pListEntry = pListEntry->Flink)
        {
           KERB_SESSION_KEY_ENTRY* pSKeyEntry = CONTAINING_RECORD(pListEntry, KERB_SESSION_KEY_ENTRY, ListEntry);

           KerbFreeSKeyEntry(pSKeyEntry);
        }
        RtlReleaseResource(&KerbNetworkServiceSKeyLock);
   }

   RtlDeleteResource(&KerbNetworkServiceSKeyLock);
}

//+-------------------------------------------------------------------------
//
//  Function:   KerbFreeLogonSessionList
//
//  Synopsis:   Frees the logon session list
//
//  Effects:
//
//  Arguments:  none
//
//  Requires:
//
//  Returns:    none
//
//  Notes:
//
//
//--------------------------------------------------------------------------

VOID
KerbFreeLogonSessionList(
    VOID
    )
{
    PKERB_LOGON_SESSION LogonSession;


    if (KerberosLogonSessionsInitialized)
    {

        KerbLockList(&KerbLogonSessionList);

        //
        // Go through the list of logon sessions and dereferences them all
        //

        while (!IsListEmpty(&KerbLogonSessionList.List))
        {
            LogonSession = CONTAINING_RECORD(
                            KerbLogonSessionList.List.Flink,
                            KERB_LOGON_SESSION,
                            ListEntry.Next
                            );

            KerbReferenceListEntry(
                &KerbLogonSessionList,
                &LogonSession->ListEntry,
                TRUE
                );

            KerbDereferenceLogonSession(LogonSession);

        }

        KerbFreeList(&KerbLogonSessionList);
    }
}

//+-------------------------------------------------------------------------
//
//  Function:   KerbAllocateLogonSession
//
//  Synopsis:   Allocates a logon session structure
//
//  Effects:    Allocates a logon session, but does not add it to the
//              list of logon sessions
//
//  Arguments:  NewLogonSession - receives a new logon session allocated
//                  with KerbAllocate
//
//  Requires:
//
//  Returns:    STATUS_SUCCESS on success
//              STATUS_INSUFFICIENT_RESOURCES if the allocation fails
//
//  Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
KerbAllocateLogonSession(
    PKERB_LOGON_SESSION * NewLogonSession
    )
{
    NTSTATUS Status = STATUS_SUCCESS;
    PKERB_LOGON_SESSION LogonSession;

    LogonSession = (PKERB_LOGON_SESSION) KerbAllocate(
                        sizeof(KERB_LOGON_SESSION)
                        );
    if (LogonSession == NULL)
    {
        return(STATUS_INSUFFICIENT_RESOURCES);
    }

    //
    // Set the references to 1 since we are returning a pointer to the
    // logon session
    //

    KerbInitializeListEntry(
        &LogonSession->ListEntry
        );

    InitializeListHead(&LogonSession->SspCredentials);

    //
    // Initialize the ticket caches
    //

    KerbInitTicketCache(&LogonSession->PrimaryCredentials.ServerTicketCache);
    KerbInitTicketCache(&LogonSession->PrimaryCredentials.AuthenticationTicketCache);
    KerbInitTicketCache(&LogonSession->PrimaryCredentials.S4UTicketCache);

    Status = RtlInitializeCriticalSection(&LogonSession->Lock);
    if (!NT_SUCCESS(Status))
    {
        KerbFree(LogonSession);
    }
    else
    {
        *NewLogonSession = LogonSession;
    }
    return(Status);
}


//+-------------------------------------------------------------------------
//
//  Function:   KerbInsertLogonSession
//
//  Synopsis:   Inserts a logon session into the list of logon sessions
//
//  Effects:    bumps reference count on logon session
//
//  Arguments:  LogonSession - LogonSession to insert
//
//  Requires:
//
//  Returns:    STATUS_SUCCESS always
//
//  Notes:
//
//
//--------------------------------------------------------------------------


NTSTATUS
KerbInsertLogonSession(
    IN PKERB_LOGON_SESSION LogonSession
    )
{
    KerbInsertListEntry(
        &LogonSession->ListEntry,
        &KerbLogonSessionList
        );

    return(STATUS_SUCCESS);
}


//+-------------------------------------------------------------------------
//
//  Function:   KerbReferenceLogonSession
//
//  Synopsis:   Locates a logon session from the logon ID and references it
//
//  Effects:    Increments reference count and possible unlinks it from list
//
//  Arguments:  LogonId - LogonId of logon session to locate
//              RemoveFromList - If TRUE, logon session will be delinked
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------


PKERB_LOGON_SESSION
KerbReferenceLogonSession(
    IN PLUID LogonId,
    IN BOOLEAN RemoveFromList
    )
{
    PLIST_ENTRY ListEntry;
    PKERB_LOGON_SESSION LogonSession = NULL;
    BOOLEAN Found = FALSE;

    KerbLockList(&KerbLogonSessionList);

    //
    // Go through the list of logon sessions looking for the correct
    // LUID
    //

    for (ListEntry = KerbLogonSessionList.List.Flink ;
         ListEntry !=  &KerbLogonSessionList.List ;
         ListEntry = ListEntry->Flink )
    {
        LogonSession = CONTAINING_RECORD(ListEntry, KERB_LOGON_SESSION, ListEntry.Next);
        if (RtlEqualLuid(
                &LogonSession->LogonId,
                LogonId
                ) )
        {
            D_DebugLog((DEB_TRACE_LSESS,"Referencing session 0x%x:0x%x, Remove=%d\n",
                LogonSession->LogonId.HighPart,
                LogonSession->LogonId.LowPart,
                RemoveFromList
                ));

            KerbReferenceListEntry(
                &KerbLogonSessionList,
                &LogonSession->ListEntry,
                RemoveFromList
                );
            Found = TRUE;
            break;
        }

    }
    if (!Found)
    {
        LogonSession = NULL;
    }
    KerbUnlockList(&KerbLogonSessionList);
    return(LogonSession);
}


//+-------------------------------------------------------------------------
//
//  Function:   KerbFreeLogonSession
//
//  Synopsis:   Frees a logon session and all associated data
//
//  Effects:
//
//  Arguments:  LogonSession
//
//  Requires:   the logon session must already be unlinked
//
//  Returns:    none
//
//  Notes:
//
//
//--------------------------------------------------------------------------


VOID
KerbFreeLogonSession(
    IN PKERB_LOGON_SESSION LogonSession
    )
{
    DsysAssert((LogonSession->ListEntry.Next.Flink == NULL) &&
           (LogonSession->ListEntry.Next.Blink == NULL));

    // Don't purge creds, as there isn't a ref-count for the credential in
    // the logon session list,and there might be outstanding handles to your
    // credentials in a local system process.
    //KerbPurgeCredentials(&LogonSession->SspCredentials);
    KerbPurgeTicketCache(&LogonSession->PrimaryCredentials.ServerTicketCache);
    KerbPurgeTicketCache(&LogonSession->PrimaryCredentials.AuthenticationTicketCache);

    if (LogonSession->PrimaryCredentials.Passwords != NULL)
    {
        KerbFreeStoredCred(LogonSession->PrimaryCredentials.Passwords);
    }

    if (LogonSession->PrimaryCredentials.OldPasswords != NULL)
    {
        KerbFreeStoredCred(LogonSession->PrimaryCredentials.OldPasswords);
    }

    KerbFreeString(&LogonSession->PrimaryCredentials.UserName);
    KerbFreeString(&LogonSession->PrimaryCredentials.DomainName);

    if (LogonSession->PrimaryCredentials.ClearPassword.Buffer != NULL)
    {
        RtlZeroMemory(
            LogonSession->PrimaryCredentials.ClearPassword.Buffer,
            LogonSession->PrimaryCredentials.ClearPassword.Length
            );
        KerbFreeString(&LogonSession->PrimaryCredentials.ClearPassword);
    }

    if (LogonSession->PrimaryCredentials.PublicKeyCreds != NULL)
    {

#ifndef WIN32_CHICAGO
        KerbReleasePkCreds(
            LogonSession,
            NULL
            );
#endif // WIN32_CHICAGO
    }

    if ((LogonSession->LogonSessionFlags & KERB_LOGON_CREDMAN_INITIALIZED) != 0)
    {
        RtlDeleteCriticalSection(&LogonSession->CredmanCredentials.Lock);
    }


    RtlDeleteCriticalSection(&LogonSession->Lock);
    KerbFree(LogonSession);
}

//+-------------------------------------------------------------------------
//
//  Function:   KerbDereferenceLogonSession
//
//  Synopsis:   Dereferences a logon session - if reference count goes
//              to zero it frees the logon session
//
//  Effects:    decrements reference count
//
//  Arguments:  LogonSession - Logon session to dereference
//
//  Requires:
//
//  Returns:    none
//
//  Notes:
//
//
//--------------------------------------------------------------------------


VOID
KerbDereferenceLogonSession(
    IN PKERB_LOGON_SESSION LogonSession
    )
{
    if (KerbDereferenceListEntry(
            &LogonSession->ListEntry,
            &KerbLogonSessionList
            ) )
    {
        D_DebugLog((DEB_TRACE_LSESS,"Dereferencing and freeing logon session 0x%x:0x%x\n",
            LogonSession->LogonId.HighPart,
            LogonSession->LogonId.HighPart
            ));
        KerbFreeLogonSession( LogonSession );
    }
}


//+-------------------------------------------------------------------------
//
//  Function:   KerbReferenceLogonSessionByPointer
//
//  Synopsis:   References a LogonSession by the LogonSession pointer itself.
//
//  Effects:    Increments reference count and possible unlinks it from list
//
//  Arguments:  LogonSession - The LogonSession to reference.
//              RemoveFromList - If TRUE, LogonSession will be delinked
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------


VOID
KerbReferenceLogonSessionByPointer(
    IN PKERB_LOGON_SESSION LogonSession,
    IN BOOLEAN RemoveFromList
    )
{

    KerbLockList(&KerbLogonSessionList);

    KerbReferenceListEntry(
        &KerbLogonSessionList,
        &LogonSession->ListEntry,
        RemoveFromList
        );

    KerbUnlockList(&KerbLogonSessionList);

}

//+-------------------------------------------------------------------------
//
//  Function:   KerbGetSaltForEtype
//
//  Synopsis:   Looks in the list of salt for an etype & returns it if found
//
//  Effects:    Allocate the output string
//
//  Arguments:  EncryptionType - etype searched for
//              EtypeInfo - List of etypes
//              DefaultSalt - salt to use if none provided
//              Salt - receives the salt to use. On error, no key should be
//                      generated.
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

NTSTATUS
KerbGetSaltForEtype(
    IN ULONG EncryptionType,
    IN OPTIONAL PKERB_ETYPE_INFO EtypeInfo,
    IN OPTIONAL PKERB_STORED_CREDENTIAL PasswordList,
    IN PUNICODE_STRING DefaultSalt,
    OUT PUNICODE_STRING SaltToUse
    )
{
    PKERB_ETYPE_INFO ListEntry;
    STRING TempString;

    //
    // If there is no etype, just use the default
    //

    if (EtypeInfo == NULL)
    {
        //
        // If we have a password list, get the salt from that.
        //

        if (ARGUMENT_PRESENT(PasswordList))
        {
            ULONG Index;
            for (Index = 0; Index < PasswordList->CredentialCount ; Index++ )
            {
                if (PasswordList->Credentials[Index].Key.keytype == (int) EncryptionType)
                {
                    if (PasswordList->Credentials[Index].Salt.Buffer != NULL)
                    {
                        return(KerbDuplicateString(
                                SaltToUse,
                                &PasswordList->Credentials[Index].Salt
                                ));
                    }
                    else if (PasswordList->DefaultSalt.Buffer != NULL)
                    {
                        return(KerbDuplicateString(
                                SaltToUse,
                                &PasswordList->DefaultSalt
                                ));
                    }
                    break;
                }
            }
        }

        //
        // otherise return the default
        //

        return(KerbDuplicateString(
                    SaltToUse,
                    DefaultSalt
                    ));
    }

    //
    // Otherwise, only return salt if the etype is in the list.
    //

    for (ListEntry = EtypeInfo; ListEntry != NULL ; ListEntry = ListEntry->next )
    {
        //
        // First check for the encryption type we want.
        //

        if (ListEntry->value.encryption_type == (int) EncryptionType)
        {

            //
            // if it has salt, return that.
            //

            if ((ListEntry->value.bit_mask & salt_present) != 0)
            {
                KERBERR KerbErr;
                TempString.Buffer = (PCHAR) ListEntry->value.salt.value;
                TempString.Length = (USHORT) ListEntry->value.salt.length;
                TempString.MaximumLength = (USHORT) ListEntry->value.salt.length;

                KerbErr = KerbStringToUnicodeString(
                            SaltToUse,
                            &TempString
                            );
                return(KerbMapKerbError(KerbErr));
            }
            else
            {
                //
                // Otherwise return the default
                //

                return(KerbDuplicateString(
                        SaltToUse,
                        DefaultSalt
                        ));
            }
        }
    }
    return(STATUS_OBJECT_NAME_NOT_FOUND);
}





//+-------------------------------------------------------------------------
//
//  Function:   KerbFreeStoredCred
//
//  Synopsis:   Frees a KERB_STORED_CREDENTIAL
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

VOID
KerbFreeStoredCred(
    IN PKERB_STORED_CREDENTIAL StoredCred
    )
{
    USHORT Index;
    for (Index = 0; Index < StoredCred->CredentialCount + StoredCred->OldCredentialCount ; Index++ )
    {
        if (StoredCred->Credentials[Index].Salt.Buffer != NULL)
        {
            KerbFreeString(&StoredCred->Credentials[Index].Salt);
        }
    }
    KerbFree(StoredCred);
}

//+-------------------------------------------------------------------------
//
//  Function:   KerbBuildPasswordList
//
//  Synopsis:   Builds a list of passwords for a logged on user
//
//  Effects:    allocates memory
//
//  Arguments:  Password - clear or OWF password
//              PasswordFlags - Indicates whether the password is clear or OWF
//              PasswordList - Receives new password list
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

NTSTATUS
KerbBuildPasswordList(
    IN PUNICODE_STRING Password,
    IN PUNICODE_STRING UserName,
    IN PUNICODE_STRING DomainName,
    IN PKERB_ETYPE_INFO SuppliedSalt,
    IN PKERB_STORED_CREDENTIAL OldPasswords,
    IN OPTIONAL PUNICODE_STRING PrincipalName,
    IN KERB_ACCOUNT_TYPE AccountType,
    IN ULONG PasswordFlags,
    OUT PKERB_STORED_CREDENTIAL * PasswordList
    )
{
    NTSTATUS Status = STATUS_SUCCESS;
    ULONG CryptTypes[KERB_MAX_CRYPTO_SYSTEMS];
    ULONG CryptCount = 0 ;
    PKERB_STORED_CREDENTIAL Credentials = NULL;
    UNICODE_STRING KeySalt = {0};
    UNICODE_STRING DefaultSalt = {0};
    ULONG CredentialSize = 0;
    ULONG CredentialCount = 0;
    PCRYPTO_SYSTEM CryptoSystem;
    ULONG Index, CredentialIndex = 0;
    PUCHAR Base;
    ULONG Offset;
    KERBERR KerbErr;

    *PasswordList = NULL;

    //
    // If we were passed an OWF, then there is just one password
    //

    if ((PasswordFlags & PRIMARY_CRED_OWF_PASSWORD) != 0)
    {
        CredentialSize += Password->Length + sizeof(KERB_KEY_DATA);
        CredentialCount++;
#ifndef DONT_SUPPORT_OLD_TYPES
        CredentialSize += Password->Length + sizeof(KERB_KEY_DATA);
        CredentialCount++;
#endif
    }
    else if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
    {

        //
        // Build the key salt.
        //

        KerbErr = KerbBuildKeySalt(
                    DomainName,
                    (ARGUMENT_PRESENT(PrincipalName) && PrincipalName->Length != 0) ? PrincipalName : UserName,
                    (ARGUMENT_PRESENT(PrincipalName) && PrincipalName->Length != 0) ? UnknownAccount : AccountType,
                    &DefaultSalt
                    );
        if (!KERB_SUCCESS(KerbErr))
        {
            D_DebugLog((DEB_ERROR,"Can't build salt. Might as well fail here\n"));
            Status = KerbMapKerbError(KerbErr);
            goto Cleanup;
        }

        //
        // For a cleartext password, build a list of encryption types and
        // create a key for each one
        //

        Status = CDBuildIntegrityVect(
                    &CryptCount,
                    CryptTypes
                    );
        if (!NT_SUCCESS(Status))
        {
            D_DebugLog((DEB_ERROR,"Can't build a list of encryption types: 0x%x.\n",Status));
            goto Cleanup;
        }
        DsysAssert(CryptCount <= KERB_MAX_CRYPTO_SYSTEMS);

        //
        // Now find the size of the key for each crypto system
        //

        for (Index = 0; Index < CryptCount; Index++ )
        {
            Status = CDLocateCSystem(
                        CryptTypes[Index],
                        &CryptoSystem
                        );
            if (!NT_SUCCESS(Status))
            {
                D_DebugLog((DEB_ERROR,"Failed to load %d crypt system: 0x%x.\n",CryptTypes[Index],Status));
                goto Cleanup;
            }

            if (((CryptoSystem->Attributes & CSYSTEM_USE_PRINCIPAL_NAME) == 0) ||
                (DefaultSalt.Buffer != NULL ))
            {
                CredentialSize += sizeof(KERB_KEY_DATA) + CryptoSystem->KeySize;
                CredentialCount++;
            }
        }

    }
    else
    {
        //
        // No flags set, so nothing we can do.
        //
        D_DebugLog((DEB_WARN,"Password passed but no flags set\n"));
        return(STATUS_SUCCESS);
    }

#ifdef notdef
    //
    // Add the space for the salt
    //

    CredentialSize += DefaultSalt.Length;

#endif

    //
    // Add in the size of the base structure
    //

    CredentialSize += FIELD_OFFSET(KERB_STORED_CREDENTIAL,Credentials);
    Credentials = (PKERB_STORED_CREDENTIAL) KerbAllocate(CredentialSize);
    if (Credentials == NULL)
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }

    //
    // Fill in the base structure
    //

    Credentials->Revision = KERB_PRIMARY_CRED_REVISION;
    Credentials->Flags = 0;
    Credentials->OldCredentialCount = 0;


    //
    // Now fill in the individual keys
    //

    Base = (PUCHAR) Credentials;
    Offset = FIELD_OFFSET(KERB_STORED_CREDENTIAL,Credentials) +
                CredentialCount * sizeof(KERB_KEY_DATA);

#ifdef notdef
    //
    // Add the default salt
    //

    Credentials->DefaultSalt = DefaultSalt;
    Credentials->DefaultSalt.Buffer = (LPWSTR) Base+Offset;

    RtlCopyMemory(
        Base + Offset,
        DefaultSalt.Buffer,
        DefaultSalt.Length
        );
    Offset += Credentials->DefaultSalt.Length;

#endif

    if ((PasswordFlags & PRIMARY_CRED_OWF_PASSWORD) != 0)
    {
        RtlCopyMemory(
            Base + Offset,
            Password->Buffer,
            Password->Length
            );

        if (!KERB_SUCCESS(KerbCreateKeyFromBuffer(
                            &Credentials->Credentials[CredentialIndex].Key,
                            Base + Offset,
                            Password->Length,
                            KERB_ETYPE_RC4_HMAC_NT
                            )))
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto Cleanup;
        }
        Base += Password->Length;
        CredentialIndex++;
#ifndef DONT_SUPPORT_OLD_TYPES

        RtlCopyMemory(
            Base + Offset,
            Password->Buffer,
            Password->Length
            );

        if (!KERB_SUCCESS(KerbCreateKeyFromBuffer(
                            &Credentials->Credentials[CredentialIndex].Key,
                            Base + Offset,
                            Password->Length,
                            KERB_ETYPE_RC4_HMAC_OLD
                            )))
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto Cleanup;
        }
        Base += Password->Length;
        CredentialIndex++;
#endif
    }
    else if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
    {
        KERB_ENCRYPTION_KEY TempKey = {0};

        //
        // Now find the size of the key for each crypto system
        //

        for (Index = 0; Index < CryptCount; Index++ )
        {
            CryptoSystem = NULL;
            Status = CDLocateCSystem(
                         CryptTypes[Index],
                         &CryptoSystem
                         );

            if (!NT_SUCCESS(Status))
            {
                Status = STATUS_SUCCESS;
                continue;
            }

            Status = KerbGetSaltForEtype(
                        CryptTypes[Index],
                        SuppliedSalt,
                        OldPasswords,
                        &DefaultSalt,
                        &KeySalt
                        );

            if (!NT_SUCCESS(Status))
            {
                //
                // This probably means that the etype wasn't supported by
                // the kdc
                //

                Status = STATUS_SUCCESS;
                continue;
            }

            //
            // If we don't have salt, skip this crypt system
            //

            if (((CryptoSystem->Attributes & CSYSTEM_USE_PRINCIPAL_NAME) != 0) &&
                (KeySalt.Buffer == NULL ))
            {
                continue;
            }

            KerbErr = KerbHashPasswordEx(
                        Password,
                        &KeySalt,
                        CryptTypes[Index],
                        &TempKey
                        );


            if (!KERB_SUCCESS(KerbErr))
            {
                //
                // It is possible that the password can't be used for every
                // encryption scheme, so skip failures
                //

                D_DebugLog((DEB_WARN, "Failed to hash pasword %wZ with type 0x%x\n",
                    Password,CryptTypes[Index] ));
                KerbFreeString(&KeySalt);
                continue;
            }


            DsysAssert(CryptoSystem->KeySize >= TempKey.keyvalue.length);


            //
            // Copy the salt and key data into the credentials
            //

            Credentials->Credentials[CredentialIndex].Salt = KeySalt;

            RtlInitUnicodeString(
                &KeySalt,
                0
                );
            Credentials->Credentials[CredentialIndex].Key = TempKey;
            Credentials->Credentials[CredentialIndex].Key.keyvalue.value = Base + Offset;

            RtlCopyMemory(
                Base + Offset,
                TempKey.keyvalue.value,
                TempKey.keyvalue.length
                );

            Offset += TempKey.keyvalue.length;

            KerbFreeKey(
                &TempKey
                );


            CredentialIndex++;
        }

    }
    Credentials->CredentialCount = (USHORT) CredentialIndex;
    *PasswordList = Credentials;
    Credentials = NULL;

Cleanup:
    if (Credentials != NULL)
    {
         KerbFreeStoredCred(Credentials);
    }
    KerbFreeString(&KeySalt);
    KerbFreeString(&DefaultSalt);

    return(Status);
}




//+-------------------------------------------------------------------------
//
//  Function:   KerbCreatePrimaryCredentials
//
//  Synopsis:   Fills in a new primary credentials structure
//
//  Effects:    allocates space for the user name and domain name
//
//  Arguments:  AccountName - Account name of this user
//              DomainName - domain name of this user
//              Password - Optionally contains a kereros hash of the password
//                      Note: if present, it is used and zeroed out.
//              PrimaryCredentials - contains structure to fill in.
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

NTSTATUS
KerbCreatePrimaryCredentials(
    IN PUNICODE_STRING AccountName,
    IN PUNICODE_STRING DomainName,
    IN OPTIONAL PUNICODE_STRING Password,
    IN OPTIONAL PUNICODE_STRING OldPassword,
    IN ULONG PasswordFlags,
    IN PLUID LogonId,
    OUT PKERB_PRIMARY_CREDENTIAL PrimaryCredentials
    )
{
    NTSTATUS Status;
    LUID SystemLogonId = SYSTEM_LUID;
    BOOLEAN IsSystemLogon = FALSE;
    BOOLEAN IsPersonal = KerbRunningPersonal();


    //
    // We can only accept account name / service name / pwds of max_unicode_length
    // -1, because we NULL terminate these for later DES derivation, and the
    // input buffers from LogonUser may not be NULL terminated.
    //
    if ((AccountName->Length > KERB_MAX_UNICODE_STRING) ||
        (DomainName->Length > KERB_MAX_UNICODE_STRING))

    {
        return (STATUS_NAME_TOO_LONG);
    }

    if ((ARGUMENT_PRESENT(Password) && (Password->Length > KERB_MAX_UNICODE_STRING)) ||
        (ARGUMENT_PRESENT(OldPassword) && (OldPassword->Length > KERB_MAX_UNICODE_STRING)))
    {
        return (STATUS_NAME_TOO_LONG);
    }

    if (RtlEqualLuid(
            &SystemLogonId,
            LogonId))
    {
        IsSystemLogon = TRUE;
    }

    Status = KerbDuplicateString(
                &PrimaryCredentials->UserName,
                AccountName
                );
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    //
    // The system logon always comes in uppercase, so lowercase it.
    //

    if (IsSystemLogon)
    {
        RtlDowncaseUnicodeString(
            &PrimaryCredentials->UserName,
            &PrimaryCredentials->UserName,
            FALSE
            );

    }
    Status = KerbDuplicateString(
                &PrimaryCredentials->DomainName,
                DomainName
                );

    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    //
    //  Neuter personal so it can't act as a server,
    //  even if someone has hacked in a machine pwd.
    //
    if (IsSystemLogon && IsPersonal)
    {
        PrimaryCredentials->Passwords = NULL;
        PrimaryCredentials->OldPasswords = NULL;
        D_DebugLog((DEB_WARN, "Running personal - No kerberos for SYSTEM LUID\n"));
    }
    else
    {

        Status = KerbBuildPasswordList(
                        Password,
                        &PrimaryCredentials->UserName,
                        &PrimaryCredentials->DomainName,
                        NULL,                                   // no supplied salt
                        NULL,                                   // no old paswords
                        NULL,                                   // no principal name
                        IsSystemLogon ? MachineAccount : UserAccount,
                        PasswordFlags,
                        &PrimaryCredentials->Passwords
                        );
        if (!NT_SUCCESS(Status))
        {
            goto Cleanup;
        }

        if (ARGUMENT_PRESENT(OldPassword) && (OldPassword->Buffer != NULL))
        {
            Status = KerbBuildPasswordList(
                        OldPassword,
                        &PrimaryCredentials->UserName,
                        &PrimaryCredentials->DomainName,
                        NULL,                           // no supplied salt
                        PrimaryCredentials->Passwords,
                        NULL,                           // no principal name
                        IsSystemLogon ? MachineAccount : UserAccount,
                        PasswordFlags,
                        &PrimaryCredentials->OldPasswords
                        );
            if (!NT_SUCCESS(Status))
            {
                goto Cleanup;
            }
        }

        //
        // Store the clear password if necessary
        //

        if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
        {
            Status = KerbDuplicatePassword(
                        &PrimaryCredentials->ClearPassword,
                        Password
                        );
            if (NT_SUCCESS(Status))
            {
                KerbHidePassword(
                    &PrimaryCredentials->ClearPassword
                    );
            }
        }
    }

Cleanup:
    if (!NT_SUCCESS(Status))
    {
        KerbFreeString(&PrimaryCredentials->UserName);
        KerbFreeString(&PrimaryCredentials->DomainName);
        if (PrimaryCredentials->ClearPassword.Buffer != NULL)
        {
            RtlZeroMemory(
                PrimaryCredentials->ClearPassword.Buffer,
                PrimaryCredentials->ClearPassword.Length
                );
            KerbFreeString(&PrimaryCredentials->ClearPassword);
        }

    }
    return(Status);
}



//+-------------------------------------------------------------------------
//
//  Function:   KerbChangeCredentialsPassword
//
//  Synopsis:   Changes the password for a KERB_PRIMARY_CREDENTIALS -
//              copies the current password into the old password field
//              and then sets the new pasword as the primary password.
//              If no new password is provided, it just fixes up the salt.
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------


NTSTATUS
KerbChangeCredentialsPassword(
    IN PKERB_PRIMARY_CREDENTIAL PrimaryCredentials,
    IN OPTIONAL PUNICODE_STRING NewPassword,
    IN OPTIONAL PKERB_ETYPE_INFO EtypeInfo,
    IN KERB_ACCOUNT_TYPE AccountType,
    IN ULONG PasswordFlags
    )
{
    NTSTATUS Status = STATUS_SUCCESS;
    PKERB_STORED_CREDENTIAL Passwords = NULL;


    //
    // LogonSession no password was supplied, use the cleartext password
    //

    if (!ARGUMENT_PRESENT(NewPassword) && (PrimaryCredentials->ClearPassword.Buffer == NULL))
    {
        D_DebugLog((DEB_ERROR,"Can't change password without new password\n"));
        Status = STATUS_INVALID_PARAMETER;
        goto Cleanup;
    }
    if (PrimaryCredentials->ClearPassword.Buffer != NULL)
    {
        KerbRevealPassword(&PrimaryCredentials->ClearPassword);
    }

    Status = KerbBuildPasswordList(
                (ARGUMENT_PRESENT(NewPassword) ? NewPassword : &PrimaryCredentials->ClearPassword),
                &PrimaryCredentials->UserName,
                &PrimaryCredentials->DomainName,
                EtypeInfo,
                PrimaryCredentials->Passwords,
                NULL,                   // no principal name
                AccountType,
                PasswordFlags,
                &Passwords
                );
    if (!NT_SUCCESS(Status))
    {
        if (PrimaryCredentials->ClearPassword.Buffer != NULL)
        {
            KerbHidePassword(&PrimaryCredentials->ClearPassword);
        }
    }
    else if ((PasswordFlags & PRIMARY_CRED_CLEAR_PASSWORD) != 0)
    {
        KerbFreeString(&PrimaryCredentials->ClearPassword);

        Status = KerbDuplicatePassword(
                    &PrimaryCredentials->ClearPassword,
                    NewPassword
                    );
        if (NT_SUCCESS(Status))
        {
            KerbHidePassword(
                &PrimaryCredentials->ClearPassword
                );
        }
    }
    else
    {
        KerbHidePassword(
            &PrimaryCredentials->ClearPassword
            );
    }


    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    //
    // Move the current password to the old password
    //

    if (ARGUMENT_PRESENT(NewPassword))
    {
        if (PrimaryCredentials->OldPasswords != NULL)
        {
            KerbFreeStoredCred(PrimaryCredentials->OldPasswords);
        }
        PrimaryCredentials->OldPasswords = PrimaryCredentials->Passwords;
    }
    else
    {
        KerbFreeStoredCred(PrimaryCredentials->Passwords);
    }

    PrimaryCredentials->Passwords = Passwords;
    Passwords = NULL;

Cleanup:
    if (Passwords != NULL)
    {
        MIDL_user_free(Passwords);
    }
    return(Status);

}


//+-------------------------------------------------------------------------
//
//  Function:   KerbCreateLogonSession
//
//  Synopsis:   Allocates a logon session, fills in the various fields,
//              and inserts it on the logon session list
//
//  Effects:
//
//  Arguments:  LogonId - LogonId for new logon session
//              AccountName - Account name of user
//              Domain Name - Domain name of user
//              Password - password for user
//              OldPassword - Old password for user, if present
//              LogonType - Type of logon
//              NewLogonSession - Receives new logon session (referenced)
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

NTSTATUS
KerbCreateLogonSession(
    IN PLUID LogonId,
    IN PUNICODE_STRING AccountName,
    IN PUNICODE_STRING DomainName,
    IN OPTIONAL PUNICODE_STRING Password,
    IN OPTIONAL PUNICODE_STRING OldPassword,
    IN ULONG PasswordFlags,
    IN SECURITY_LOGON_TYPE LogonType,
    OUT PKERB_LOGON_SESSION * NewLogonSession
    )
{
    PKERB_LOGON_SESSION LogonSession = NULL;
    NTSTATUS Status;

    D_DebugLog((DEB_TRACE_LSESS,"Creating logon session for 0x%x:0x%x\n",
        LogonId->HighPart,LogonId->LowPart));


    //
    // Check for a logon session with the same id
    //

    LogonSession = KerbReferenceLogonSession(
                        LogonId,
                        FALSE           // don't unlink
                        );
    if (LogonSession != NULL)
    {
        //
        // We already have this logon session, so don't create another one.
        //
        KerbDereferenceLogonSession(LogonSession);

        return(STATUS_OBJECT_NAME_EXISTS);
    }
    //
    // Allocate the new logon session
    //

    Status = KerbAllocateLogonSession( &LogonSession );
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    //
    // Fill in the logon session components
    //

    LogonSession->LogonId = *LogonId;
    LogonSession->Lifetime = KerbGlobalWillNeverTime;

    //
    // All logons are deferred until proven otherwise
    //

    LogonSession->LogonSessionFlags = KERB_LOGON_DEFERRED;

    //
    // If the domain name is equal to the computer name then the logon was
    //  local.
    //

#ifndef WIN32_CHICAGO
    KerbGlobalReadLock();

    if (RtlEqualDomainName(
            DomainName,
            &KerbGlobalMachineName
            ))
    {
        LogonSession->LogonSessionFlags |= KERB_LOGON_LOCAL_ONLY;
    }

    KerbGlobalReleaseLock();
#endif // WIN32_CHICAGO

    Status = KerbCreatePrimaryCredentials(
                AccountName,
                DomainName,
                Password,
                OldPassword,
                PasswordFlags,
                LogonId,
                &LogonSession->PrimaryCredentials
                );

    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    Status = KerbInitializeList(
                &LogonSession->CredmanCredentials
                );

    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    LogonSession->LogonSessionFlags |= KERB_LOGON_CREDMAN_INITIALIZED;



    if ((LogonSession->PrimaryCredentials.Passwords == NULL) ||
        (LogonSession->PrimaryCredentials.Passwords->CredentialCount == 0))
    {
        LogonSession->LogonSessionFlags |= KERB_LOGON_NO_PASSWORD;
    }

    //
    // S4ULogon (any reason to keep this??? maybe for debug..)
    //
    if ((PasswordFlags & KERB_LOGON_S4U_SESSION) != 0)
    {
        LogonSession->LogonSessionFlags |= KERB_LOGON_S4U_SESSION;
    }




    //
    // Now that the logon session structure is filled out insert it
    // into the list. After this you need to hold the logon session lock
    // to read or write this logon session.
    //

    Status = KerbInsertLogonSession(LogonSession);
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }
    *NewLogonSession = LogonSession;

Cleanup:

    if (!NT_SUCCESS(Status))
    {
        if (NULL != LogonSession)
        {
            KerbFreeLogonSession(LogonSession);
            LogonSession = NULL;
        }
    }
    return(Status);

}


#ifndef WIN32_CHICAGO



//+-------------------------------------------------------------------------
//
//  Function:   KerbCreateLogonSessionFromKerbCred
//
//  Synopsis:   Creates a logon session from the delegation information
//              in a KERB_CRED structure. If a logon session is supplied,
//              it is updated with the supplied information.
//
//  Effects:
//
//  Arguments:  LogonId - Logon id for the logon session
//              Ticket - Ticket from the AP request containing the client's
//                      name and realm.
//              KerbCred - KERB_CRED containing the delegation tickets
//              EncryptedCred - Structure containing information about the
//                      tickets, such as session keys, flags, etc.
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------


NTSTATUS
KerbCreateLogonSessionFromKerbCred(
    IN OPTIONAL PLUID LogonId,
    IN PKERB_ENCRYPTED_TICKET Ticket,
    IN PKERB_CRED KerbCred,
    IN PKERB_ENCRYPTED_CRED EncryptedCred,
    IN OUT PKERB_LOGON_SESSION *OldLogonSession
    )
{

    PKERB_LOGON_SESSION LogonSession = NULL;
    NTSTATUS Status = STATUS_SUCCESS;
    UNICODE_STRING AccountName;
    UNICODE_STRING ShortAccountName;
    UNICODE_STRING DomainName;
    KERB_ENCRYPTED_KDC_REPLY FakeReplyBody;
    KERB_KDC_REPLY FakeReply;
    PKERB_CRED_INFO CredInfo;
    PKERB_CRED_TICKET_LIST TicketList;
    PKERB_CRED_INFO_LIST CredInfoList;
    PKERB_TICKET_CACHE_ENTRY TicketCacheEntry;
    ULONG NameType;
    LPWSTR LastSlash;
    BOOLEAN LogonSessionLocked = FALSE;
    BOOLEAN CreatedLogonSession = TRUE;
    PKERB_TICKET_CACHE TicketCache = NULL;
    ULONG TgtFlags = KERB_TICKET_CACHE_PRIMARY_TGT;
    ULONG CacheFlags = 0;

    AccountName.Buffer = NULL;
    DomainName.Buffer = NULL;
    if (ARGUMENT_PRESENT(LogonId))
    {
        D_DebugLog((DEB_TRACE_LSESS,"Creating logon session for 0x%x:0x%x\n",
            LogonId->HighPart,LogonId->LowPart));
    }


    if (!KERB_SUCCESS(KerbConvertPrincipalNameToString(
            &AccountName,
            &NameType,
            &Ticket->client_name
            )))
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }

    //
    // We need to strip off everything before the last '\' in case there
    // was a domain name.
    //

    LastSlash = wcsrchr(AccountName.Buffer, L'\\');
    if (LastSlash != NULL)
    {
        ShortAccountName.Buffer = LastSlash+1;
        RtlInitUnicodeString(
            &ShortAccountName,
            ShortAccountName.Buffer
            );
    }
    else
    {
        ShortAccountName = AccountName;
    }
    if (!KERB_SUCCESS(KerbConvertRealmToUnicodeString(
            &DomainName,
            &Ticket->client_realm
            )))
    {
        Status = STATUS_INSUFFICIENT_RESOURCES;
        goto Cleanup;
    }

    D_DebugLog((DEB_TRACE, "Creating delegation logon session for %wZ \\ %wZ\n",
        &DomainName, &ShortAccountName ));

    if ((*OldLogonSession) == NULL)
    {
        //
        // Allocate the new logon session
        //

        Status = KerbAllocateLogonSession( &LogonSession );
        if (!NT_SUCCESS(Status))
        {
            goto Cleanup;
        }

        //
        // Fill in the logon session components
        //

        LogonSession->LogonId = *LogonId;
        LogonSession->Lifetime = KerbGlobalWillNeverTime;


        Status = KerbCreatePrimaryCredentials(
                    &ShortAccountName,
                    &DomainName,
                    NULL,   // no password
                    NULL,   // no old password
                    0,      // no flags
                    LogonId,
                    &LogonSession->PrimaryCredentials
                    );

        if (!NT_SUCCESS(Status))
        {
            goto Cleanup;
        }


        LogonSession->LogonSessionFlags |= KERB_LOGON_NO_PASSWORD;
    }
    else
    {
        CreatedLogonSession = FALSE;
        KerbWriteLockLogonSessions(*OldLogonSession);
        LogonSessionLocked = TRUE;
        LogonSession = *OldLogonSession;

        //
        // If the user name & domain name are blank, update them from the
        // ticket.
        //

        if (LogonSession->PrimaryCredentials.UserName.Length == 0)
        {
            KerbFreeString(&LogonSession->PrimaryCredentials.UserName);
            LogonSession->PrimaryCredentials.UserName = AccountName;
            AccountName.Buffer = NULL;
        }
        if (LogonSession->PrimaryCredentials.DomainName.Length == 0)
        {
            KerbFreeString(&LogonSession->PrimaryCredentials.DomainName);
            LogonSession->PrimaryCredentials.DomainName = DomainName;
            DomainName.Buffer = NULL;
        }
    }

    //
    // Now stick the ticket into the ticket cache. First build up a fake
    // KDC reply message from the encryped cred info.
    //

    TicketList = KerbCred->tickets;
    CredInfoList = EncryptedCred->ticket_info;

    while (TicketList != NULL)
    {
        TimeStamp Endtime = {0};

        if (CredInfoList == NULL)
        {

            D_DebugLog((DEB_ERROR, "No ticket info in encrypted cred. %ws, line %d\n", THIS_FILE, __LINE__));
            Status = STATUS_INVALID_PARAMETER;
            goto Cleanup;
        }
        CredInfo = &CredInfoList->value;

        //
        // Set the lifetime to the end or renew_until of the longest lived ticket
        //

        if ((CredInfo->bit_mask & KERB_CRED_INFO_renew_until_present) != 0)
        {
            KerbConvertGeneralizedTimeToLargeInt(
                &Endtime,
                &CredInfo->KERB_CRED_INFO_renew_until,
                0                                   // no usec
                );
        }
        else if ((CredInfo->bit_mask & endtime_present) != 0)
        {
            KerbConvertGeneralizedTimeToLargeInt(
                &Endtime,
                &CredInfo->endtime,
                0                                   // no usec
                );
        }

        if (Endtime.QuadPart != 0)
        {
            if (LogonSession->Lifetime.QuadPart == KerbGlobalWillNeverTime.QuadPart)
            {
                LogonSession->Lifetime.QuadPart = Endtime.QuadPart;
            }
            else
            {
                LogonSession->Lifetime.QuadPart = max(LogonSession->Lifetime.QuadPart,Endtime.QuadPart);
            }
        }



        RtlZeroMemory(
            &FakeReplyBody,
            sizeof(KERB_ENCRYPTED_KDC_REPLY)
            );

        FakeReplyBody.session_key = CredInfo->key;
        FakeReplyBody.nonce = 0;

        //
        // Set the ticket flags
        //

        if (CredInfo->bit_mask & flags_present)
        {
            FakeReplyBody.flags = CredInfo->flags;
        }
        else
        {
            FakeReplyBody.flags.length = 0;
            FakeReplyBody.flags.value = NULL;
        }

        FakeReplyBody.authtime = Ticket->authtime;

        if (CredInfo->bit_mask & KERB_CRED_INFO_starttime_present)
        {
            FakeReplyBody.KERB_ENCRYPTED_KDC_REPLY_starttime =
                CredInfo->KERB_CRED_INFO_starttime;
            FakeReplyBody.bit_mask |= KERB_ENCRYPTED_KDC_REPLY_starttime_present;
        }

        //
        // If an end time was sent, use it, otherwise assume the ticket
        // lasts forever
        //

        if (CredInfo->bit_mask & endtime_present)
        {
            FakeReplyBody.endtime =
                CredInfo->endtime;
        }
        else
        {
            KerbConvertLargeIntToGeneralizedTime(
                &FakeReplyBody.endtime,
                NULL,
                &KerbGlobalWillNeverTime
                );
        }

        if (CredInfo->bit_mask & KERB_CRED_INFO_renew_until_present)
        {
            FakeReplyBody.KERB_ENCRYPTED_KDC_REPLY_renew_until =
                CredInfo->KERB_CRED_INFO_renew_until;
            FakeReplyBody.bit_mask |= KERB_ENCRYPTED_KDC_REPLY_renew_until_present;
        }

        FakeReplyBody.server_name = TicketList->value.server_name;
        FakeReplyBody.server_realm = TicketList->value.realm;

        //
        // Determine which ticket cache to use
        //

        if ((FakeReplyBody.server_name.name_string != NULL) &&
            _stricmp(
                FakeReplyBody.server_name.name_string->value,
                KDC_PRINCIPAL_NAME_A) == 0)
        {
            TicketCache = &LogonSession->PrimaryCredentials.AuthenticationTicketCache;
            //
            // We only want to use the primary_tgt flag the first time through
            //

            CacheFlags = TgtFlags;
            TgtFlags = 0;
            D_DebugLog((DEB_TRACE,"Adding ticket from kerb_cred to authentication ticket cache\n"));
        }
        else
        {
            TicketCache = &LogonSession->PrimaryCredentials.ServerTicketCache;
            CacheFlags = 0;
            D_DebugLog((DEB_TRACE,"Adding ticket from kerb_cred to server ticket cache\n"));
        }


        FakeReply.client_name = Ticket->client_name;
        FakeReply.ticket = TicketList->value;
        FakeReply.client_realm = Ticket->client_realm;
        Status = KerbCacheTicket(
                    TicketCache,
                    &FakeReply,
                    &FakeReplyBody,
                    NULL,               // no target name
                    NULL,
                    CacheFlags,
                    TRUE,               // link
                    &TicketCacheEntry
                    );
        if (!NT_SUCCESS(Status))
        {
            D_DebugLog((DEB_ERROR, "Failed to cache ticket: 0x%x. %ws, line %d\n",
                Status, THIS_FILE, __LINE__));
            goto Cleanup;
        }

        LogonSession->LogonSessionFlags &= ~KERB_LOGON_DEFERRED;

        KerbDereferenceTicketCacheEntry(TicketCacheEntry);

        CredInfoList = CredInfoList->next;
        TicketList = TicketList->next;
    }

    //
    // Now that the logon session structure is filled out insert it
    // into the list. After this you need to hold the logon session lock
    // to read or write this logon session.
    //

    if (*OldLogonSession == NULL)
    {
        Status = KerbInsertLogonSession(LogonSession);
        if (!NT_SUCCESS(Status))
        {
            goto Cleanup;
        }
        *OldLogonSession = LogonSession;
    }

Cleanup:


    if (LogonSessionLocked)
    {
        KerbUnlockLogonSessions(LogonSession);
    }

    if (CreatedLogonSession)
    {
        if (!NT_SUCCESS(Status))
        {
            if (LogonSession != NULL)
            {
                KerbFreeLogonSession(LogonSession);
            }
        }
    }
    KerbFreeString(&AccountName);
    KerbFreeString(&DomainName);
    return(Status);

}

#endif // WIN32_CHICAGO