//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1996
//
// File:        kerberos.cxx
//
// Contents:    main entrypoints for the Kerberos security package
//
//
// History:     16-April-1996 Created         MikeSw
//              26-Sep-1998   ChandanS
//                            Added more debugging support etc.
//
//------------------------------------------------------------------------


#include <kerb.hxx>
#define KERBP_ALLOCATE
#include <kerbp.h>
#include <userapi.h>
#include <safeboot.h>
#include <spncache.h>
#include <wow64t.h>

#ifdef RETAIL_LOG_SUPPORT
static TCHAR THIS_FILE[]=TEXT(__FILE__);
HANDLE g_hParamEvent = NULL;
HKEY   g_hKeyParams = NULL;
#endif

#ifndef WIN32_CHICAGO
#ifdef RETAIL_LOG_SUPPORT

DEFINE_DEBUG2(Kerb);
extern DWORD KSuppInfoLevel; // needed to adjust values for common2 dir
HANDLE g_hWait = NULL;

DEBUG_KEY   KerbDebugKeys[] = { {DEB_ERROR,         "Error"},
                                {DEB_WARN,          "Warn"},
                                {DEB_TRACE,         "Trace"},
                                {DEB_TRACE_API,     "API"},
                                {DEB_TRACE_CRED,    "Cred"},
                                {DEB_TRACE_CTXT,    "Ctxt"},
                                {DEB_TRACE_LSESS,   "LSess"},
                                {DEB_TRACE_LOGON,   "Logon"},
                                {DEB_TRACE_KDC,     "KDC"},
                                {DEB_TRACE_CTXT2,   "Ctxt2"},
                                {DEB_TRACE_TIME,    "Time"},
                                {DEB_TRACE_LOCKS,   "Locks"},
                                {DEB_TRACE_LEAKS,   "Leaks"},
                                {DEB_TRACE_SPN_CACHE, "SPN"},
                                {DEB_TRACE_LOOPBACK,  "LoopBack"},
                                {DEB_TRACE_U2U,       "U2U"},
                                {0,                  NULL},
                              };


VOID
KerbInitializeDebugging(
    VOID
    )
{
    KerbInitDebug(KerbDebugKeys);

}

////////////////////////////////////////////////////////////////////
//
//  Name:       KerbGetKerbRegParams
//
//  Synopsis:   Gets the debug paramaters from the registry
//
//  Arguments:  HKEY to HKLM/System/CCS/LSA/Kerberos
//
//  Notes:      Sets KerbInfolevel for debug spew
//
void
KerbGetKerbRegParams(HKEY ParamKey)
{

    DWORD       cbType, tmpInfoLevel = KerbInfoLevel, cbSize;
    DWORD       dwErr;

    cbSize = sizeof(tmpInfoLevel);

    dwErr = RegQueryValueExW(
        ParamKey,
        WSZ_KERBDEBUGLEVEL,
        NULL,
        &cbType,
        (LPBYTE)&tmpInfoLevel,
        &cbSize
        );
    if (dwErr != ERROR_SUCCESS)
    {
        if (dwErr ==  ERROR_FILE_NOT_FOUND)
        {
            // no registry value is present, don't want info
            // so reset to defaults
#if DBG
            KSuppInfoLevel = KerbInfoLevel = DEB_ERROR;

#else // fre
            KSuppInfoLevel = KerbInfoLevel = 0;
#endif
        }else{
            D_DebugLog((DEB_WARN, "Failed to query DebugLevel: 0x%x\n", dwErr));
        }

    }

    // TBD:  Validate flags?
    KSuppInfoLevel = KerbInfoLevel = tmpInfoLevel;

    cbSize = sizeof(tmpInfoLevel);

    dwErr = RegQueryValueExW(
               ParamKey,
               WSZ_FILELOG,
               NULL,
               &cbType,
               (LPBYTE)&tmpInfoLevel,
               &cbSize
               );

    if (dwErr == ERROR_SUCCESS)
    {
       KerbSetLoggingOption((BOOL) tmpInfoLevel);
    }
    else if (dwErr == ERROR_FILE_NOT_FOUND)
    {
       KerbSetLoggingOption(FALSE);
    }

    cbSize = sizeof(tmpInfoLevel);

    dwErr = RegQueryValueExW(
               ParamKey,
               KERB_PARAMETER_RETRY_PDC,
               NULL,
               &cbType,
               (LPBYTE)&tmpInfoLevel,
               &cbSize
               );

    if (dwErr == ERROR_SUCCESS)
    {
        if( tmpInfoLevel != 0 )
        {
            KerbGlobalRetryPdc = TRUE;
        } else {
            KerbGlobalRetryPdc = FALSE;
        }

    }
    else if (dwErr == ERROR_FILE_NOT_FOUND)
    {
       KerbGlobalRetryPdc = FALSE;
    }


    return;
}

////////////////////////////////////////////////////////////////////
//
//  Name:       KerbWaitCleanup
//
//  Synopsis:   Cleans up wait from KerbWatchParamKey()
//
//  Arguments:  <none>
//
//  Notes:      .
//
void
KerbWaitCleanup()
{

    NTSTATUS Status = STATUS_SUCCESS;

    if (NULL != g_hWait) {
        Status = RtlDeregisterWait(g_hWait);
        if (NT_SUCCESS(Status) && NULL != g_hParamEvent ) {
            CloseHandle(g_hParamEvent);
        }
    }
}



////////////////////////////////////////////////////////////////////
//
//  Name:       KerbWatchParamKey
//
//  Synopsis:   Sets RegNotifyChangeKeyValue() on param key, initializes
//              debug level, then utilizes thread pool to wait on
//              changes to this registry key.  Enables dynamic debug
//              level changes, as this function will also be callback
//              if registry key modified.
//
//  Arguments:  pCtxt is actually a HANDLE to an event.  This event
//              will be triggered when key is modified.
//
//  Notes:      .
//
VOID
KerbWatchKerbParamKey(PVOID    pCtxt,
                  BOOLEAN  fWaitStatus)
{
    NTSTATUS    Status;
    LONG        lRes = ERROR_SUCCESS;

    if (NULL == g_hKeyParams)  // first time we've been called.
    {
        lRes = RegOpenKeyExW(
                    HKEY_LOCAL_MACHINE,
                    KERB_PARAMETER_PATH,
                    0,
                    KEY_READ,
                    &g_hKeyParams);

        if (ERROR_SUCCESS != lRes)
        {
            D_DebugLog((DEB_WARN,"Failed to open kerberos key: 0x%x\n", lRes));
            goto Reregister;
        }
    }

    if (NULL != g_hWait)
    {
        Status = RtlDeregisterWait(g_hWait);
        if (!NT_SUCCESS(Status))
        {
            D_DebugLog((DEB_WARN, "Failed to Deregister wait on registry key: 0x%x\n", Status));
            goto Reregister;
        }

    }

    lRes = RegNotifyChangeKeyValue(
                g_hKeyParams,
                FALSE,
                REG_NOTIFY_CHANGE_LAST_SET,
                (HANDLE) pCtxt,
                TRUE);

    if (ERROR_SUCCESS != lRes)
    {
        D_DebugLog((DEB_ERROR,"Debug RegNotify setup failed: 0x%x\n", lRes));
        // we're tanked now. No further notifications, so get this one
    }

    KerbGetKerbRegParams(g_hKeyParams);

Reregister:

    Status = RtlRegisterWait(&g_hWait,
                             (HANDLE) pCtxt,
                             KerbWatchKerbParamKey,
                             (HANDLE) pCtxt,
                             INFINITE,
                             WT_EXECUTEINPERSISTENTIOTHREAD|
                             WT_EXECUTEONLYONCE);

}

#endif // RETAIL_LOG_SUPPORT

NTSTATUS NTAPI
SpCleanup(
    VOID
    );


BOOL
DllMain(
    HINSTANCE Module,
    ULONG Reason,
    PVOID Context
    )
{
    if ( Reason == DLL_PROCESS_ATTACH )
    {
        DisableThreadLibraryCalls( Module );
    }
    else if ( Reason == DLL_PROCESS_DETACH )
    {
#if RETAIL_LOG_SUPPORT
        KerbUnloadDebug();
        KerbWaitCleanup();
#endif
    }

    return TRUE ;
}


//+-------------------------------------------------------------------------
//
//  Function:   SpLsaModeInitialize
//
//  Synopsis:   This function is called by the LSA when this DLL is loaded.
//              It returns security package function tables for all
//              security packages in the DLL.
//
//  Effects:
//
//  Arguments:  LsaVersion - Version number of the LSA
//              PackageVersion - Returns version number of the package
//              Tables - Returns array of function tables for the package
//              TableCount - Returns number of entries in array of
//                      function tables.
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------


NTSTATUS NTAPI
SpLsaModeInitialize(
    IN ULONG LsaVersion,
    OUT PULONG PackageVersion,
    OUT PSECPKG_FUNCTION_TABLE * Tables,
    OUT PULONG TableCount
    )
{
    KerbInitializeDebugging();

#ifdef RETAIL_LOG_SUPPORT

    g_hParamEvent = CreateEvent(NULL,
                           FALSE,
                           FALSE,
                           NULL);

    if (NULL == g_hParamEvent)
    {
        D_DebugLog((DEB_WARN, "CreateEvent for ParamEvent failed - 0x%x\n", GetLastError()));
    } else {
        KerbWatchKerbParamKey(g_hParamEvent, FALSE);
    }

#endif // RETAIL_LOG_SUPPORT

    if (LsaVersion != SECPKG_INTERFACE_VERSION)
    {
        D_DebugLog((DEB_ERROR,"Invalid LSA version: %d. %ws, line %d\n",LsaVersion, THIS_FILE, __LINE__));
        return(STATUS_INVALID_PARAMETER);
    }

    KerberosFunctionTable.InitializePackage = NULL;;
    KerberosFunctionTable.LogonUser = NULL;
    KerberosFunctionTable.CallPackage = LsaApCallPackage;
    KerberosFunctionTable.LogonTerminated = LsaApLogonTerminated;
    KerberosFunctionTable.CallPackageUntrusted = LsaApCallPackageUntrusted;
    KerberosFunctionTable.LogonUserEx2 = LsaApLogonUserEx2;
    KerberosFunctionTable.Initialize = SpInitialize;
    KerberosFunctionTable.Shutdown = SpShutdown;
    KerberosFunctionTable.GetInfo = SpGetInfo;
    KerberosFunctionTable.AcceptCredentials = SpAcceptCredentials;
    KerberosFunctionTable.AcquireCredentialsHandle = SpAcquireCredentialsHandle;
    KerberosFunctionTable.FreeCredentialsHandle = SpFreeCredentialsHandle;
    KerberosFunctionTable.QueryCredentialsAttributes = SpQueryCredentialsAttributes;
    KerberosFunctionTable.SaveCredentials = SpSaveCredentials;
    KerberosFunctionTable.GetCredentials = SpGetCredentials;
    KerberosFunctionTable.DeleteCredentials = SpDeleteCredentials;
    KerberosFunctionTable.InitLsaModeContext = SpInitLsaModeContext;
    KerberosFunctionTable.AcceptLsaModeContext = SpAcceptLsaModeContext;
    KerberosFunctionTable.DeleteContext = SpDeleteContext;
    KerberosFunctionTable.ApplyControlToken = SpApplyControlToken;
    KerberosFunctionTable.GetUserInfo = SpGetUserInfo;
    KerberosFunctionTable.GetExtendedInformation = SpGetExtendedInformation;
    KerberosFunctionTable.QueryContextAttributes = SpQueryLsaModeContextAttributes;
    KerberosFunctionTable.CallPackagePassthrough = LsaApCallPackagePassthrough;


    *PackageVersion = SECPKG_INTERFACE_VERSION;

    *TableCount = 1;
    *Tables = &KerberosFunctionTable;

    // initialize event tracing (a/k/a WMI tracing, software tracing)
    KerbInitializeTrace();

    return(STATUS_SUCCESS);
}
#endif // WIN32_CHICAGO



//+-------------------------------------------------------------------------
//
//  Function:   SpInitialize
//
//  Synopsis:   Initializes the Kerberos package
//
//  Effects:
//
//  Arguments:  PackageId - Contains ID for this package assigned by LSA
//              Parameters - Contains machine-specific information
//              FunctionTable - Contains table of LSA helper routines
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------

NTSTATUS NTAPI
SpInitialize(
    IN ULONG_PTR PackageId,
    IN PSECPKG_PARAMETERS Parameters,
    IN PLSA_SECPKG_FUNCTION_TABLE FunctionTable
    )
{
    NTSTATUS Status;
    UNICODE_STRING TempUnicodeString;
#ifndef WIN32_CHICAGO
    WCHAR SafeBootEnvVar[sizeof(SAFEBOOT_MINIMAL_STR_W) + sizeof(WCHAR)];
#endif // WIN32_CHICAGO
    UNICODE_STRING DnsString = {0};;
#ifdef WIN32_CHICAGO
    PKERB_LOGON_SESSION LogonSession = NULL;
#endif // WIN32_CHICAGO

#ifndef WIN32_CHICAGO
    RtlInitializeResource(&KerberosGlobalResource);
#endif // WIN32_CHICAGO
    KerberosPackageId = PackageId;
    LsaFunctions = FunctionTable;


#ifndef WIN32_CHICAGO
    KerberosState = KerberosLsaMode;
#else // WIN32_CHICAGO
    KerberosState = KerberosUserMode;
#endif // WIN32_CHICAGO


    RtlInitUnicodeString(
        &KerbPackageName,
        MICROSOFT_KERBEROS_NAME_W
        );

#ifndef WIN32_CHICAGO
    // Check is we are in safe boot.

    //
    // Does environment variable exist
    //

    RtlZeroMemory( SafeBootEnvVar, sizeof( SafeBootEnvVar ) );

    KerbGlobalSafeModeBootOptionPresent = FALSE;

    if ( GetEnvironmentVariable(L"SAFEBOOT_OPTION", SafeBootEnvVar, sizeof(SafeBootEnvVar)/sizeof(SafeBootEnvVar[0]) ) )
    {
        if ( !wcscmp( SafeBootEnvVar, SAFEBOOT_MINIMAL_STR_W ) )
        {
            KerbGlobalSafeModeBootOptionPresent = TRUE;
        }
    }
#endif // WIN32_CHICAGO


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

    //
    // Init data for the kdc calling routine
    //

#ifndef WIN32_CHICAGO
    Status = KerbInitKdcData();
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }


    //
    // init global LSA policy handle.
    //

    Status = LsaIOpenPolicyTrusted(
                &KerbGlobalPolicyHandle
                );

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

#endif // WIN32_CHICAGO

    //
    // Get information about encryption
    //

    if ((Parameters->MachineState & SECPKG_STATE_ENCRYPTION_PERMITTED) != 0)
    {
        KerbGlobalEncryptionPermitted = TRUE;
    }
    if ((Parameters->MachineState & SECPKG_STATE_STRONG_ENCRYPTION_PERMITTED) != 0)
    {
        KerbGlobalStrongEncryptionPermitted = TRUE;
    }

    //
    // Get our global role
    //

    if ((Parameters->MachineState & SECPKG_STATE_DOMAIN_CONTROLLER) != 0)
    {
        //
        // We will behave like a member workstation/server until the DS
        // says we are ready to act as a DC
        //

        KerbGlobalRole = KerbRoleWorkstation;
    }
    else if ((Parameters->MachineState & SECPKG_STATE_WORKSTATION) != 0)
    {
        KerbGlobalRole = KerbRoleWorkstation;
    }
    else
    {
        KerbGlobalRole = KerbRoleStandalone;
    }

    //
    // Fill in various useful constants
    //

    KerbSetTime(&KerbGlobalWillNeverTime, MAXTIMEQUADPART);
    KerbSetTime(&KerbGlobalHasNeverTime, 0);

    //
    // compute blank password hashes.
    //

    Status = RtlCalculateLmOwfPassword( "", &KerbGlobalNullLmOwfPassword );
    ASSERT( NT_SUCCESS(Status) );

    RtlInitUnicodeString(&TempUnicodeString, NULL);
    Status = RtlCalculateNtOwfPassword(&TempUnicodeString,
                                       &KerbGlobalNullNtOwfPassword);
    ASSERT( NT_SUCCESS(Status) );

    RtlInitUnicodeString(
        &KerbGlobalKdcServiceName,
        KDC_PRINCIPAL_NAME
        );

    //
    // At some point we may want to read the registry here to
    // find out whether we need to enforce times, currently times
    // are always enforced.
    //

    KerbGlobalEnforceTime = FALSE;

    //
    // Get the machine Name
    //


    Status = KerbSetComputerName();

    if( !NT_SUCCESS(Status) )
    {
        D_DebugLog((DEB_ERROR,"KerbSetComputerName failed\n"));
        goto Cleanup;
    }

    //
    // Initialize the logon session list. This has to be done because
    // KerbSetDomainName will try to acess the logon session list
    //

    Status = KerbInitLogonSessionList();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR,"Failed to initialize logon session list: 0x%x. %ws, line %d\n",
            Status, THIS_FILE, __LINE__ ));
        goto Cleanup;
    }

    Status = KerbInitNetworkServiceLoopbackDetection();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR, "Failed to initialize network service loopback detection: 0x%x. %ws, line %d\n",
            Status, THIS_FILE, __LINE__ ));
        goto Cleanup;
    }

    Status = KerbCreateSKeyTimer();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR, "Failed to initialize network service session key list timer: 0x%x. %ws, line %d\n",
            Status, THIS_FILE, __LINE__ ));
        goto Cleanup;
    }

    Status = KerbInitTicketHandling();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR,"Failed to initialize ticket handling: 0x%x. %ws, line %d\n",
            Status, THIS_FILE, __LINE__));
        goto Cleanup;
    }

#ifndef WIN32_CHICAGO
    Status = KerbInitializeLogonSidCache();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR,"Failed to initialize sid cache: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
        goto Cleanup;
    }
#endif


    //
    // Update all global structures referencing the domain name
    //

    Status = KerbSetDomainName(
                &Parameters->DomainName,
                &Parameters->DnsDomainName,
                Parameters->DomainSid,
                Parameters->DomainGuid
                );
    if (!NT_SUCCESS(Status))
    {
        goto Cleanup;
    }

    //
    // Initialize the internal Kerberos lists
    //


    Status = KerbInitCredentialList();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR,"Failed to initialize credential list: 0x%x. %ws, line %d\n",
            Status, THIS_FILE, __LINE__ ));
        goto Cleanup;
    }

    Status = KerbInitContextList();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR,"Failed to initialize context list: 0x%x. %ws, line %d\n",
            Status, THIS_FILE, __LINE__ ));
        goto Cleanup;
    }
    Status = KerbInitTicketCaching();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR,"Failed to initialize ticket cache: 0x%x. %ws, line %d\n",
            Status, THIS_FILE, __LINE__));
        goto Cleanup;

    }

    Status = KerbInitBindingCache();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR,"Failed to initialize binding cache: 0x%x. %ws, line %d\n",
            Status, THIS_FILE, __LINE__));
        goto Cleanup;
    }

    Status = KerbInitSpnCache();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR,"Failed to initialize SPN cache: 0x%x. %ws, line %d\n",
                  Status, THIS_FILE, __LINE__));
        goto Cleanup;
    }

    Status = KerbInitializeMitRealmList();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR,"Failed to initialize MIT realm list: 0x%x. %ws, line %d\n",
            Status, THIS_FILE, __LINE__ ));
        goto Cleanup;
    }

#ifndef WIN32_CHICAGO
    Status = KerbInitializePkinit();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR,"Failed to initialize PKINT: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
        goto Cleanup;
    }
#endif // WIN32_CHICAGO

    Status = KerbInitializeSockets(
                MAKEWORD(1,1),          // we want version 1.1
                1,                      // we need at least 1 socket
                &KerbGlobalNoTcpUdp
                );

    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR,"Failed to initialize sockets: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
        goto Cleanup;
    }

#ifndef WIN32_CHICAGO
    Status = KerbRegisterForDomainChange();
    if (!NT_SUCCESS(Status))
    {
        D_DebugLog((DEB_ERROR, "Failed to register for domain change notification: 0x%x. %ws, line %d\n",Status, THIS_FILE, __LINE__));
        goto Cleanup;
    }

    //
    // Check to see if there is a CSP registered for replacing the StringToKey calculation
    //
    CheckForOutsideStringToKey();

    //
    // Register to update the machine sid cache
    //

    KerbWaitGetMachineSid();

    //
    // See if there are any "join hints" to process
    //
    ReadInitialDcRecord(
       &KerbGlobalInitialDcRecord,
       &KerbGlobalInitialDcAddressType,
       &KerbGlobalInitialDcFlags
       );


#endif // WIN32_CHICAGO

    KerbGlobalSocketsInitialized = TRUE;
    KerbGlobalInitialized = TRUE;

Cleanup:
    KerbFreeString(
        &DnsString
        );
    //
    // If we failed to initialize, shutdown
    //

    if (!NT_SUCCESS(Status))
    {
        SpCleanup();
    }

    return(Status);
}


//+-------------------------------------------------------------------------
//
//  Function:   SpCleanup
//
//  Synopsis:   Function to shutdown the Kerberos package.
//
//  Effects:    Forces the freeing of all credentials, contexts and
//              logon sessions and frees all global data
//
//  Arguments:  none
//
//  Requires:
//
//  Returns:
//
//  Notes:      STATUS_SUCCESS in all cases
//
//
//--------------------------------------------------------------------------


NTSTATUS NTAPI
SpCleanup(
    VOID
    )
{
    KerbGlobalInitialized = FALSE;
#ifndef WIN32_CHICAGO
    KerbUnregisterForDomainChange();
#endif // WIN32_CHICAGO
    KerbFreeLogonSessionList();
    KerbFreeContextList();
    KerbFreeTicketCache();
    // KerbFreeCredentialList();

    KerbFreeNetworkServiceSKeyListAndLock();
    KerbFreeSKeyTimer();

    KerbFreeString(&KerbGlobalDomainName);
    KerbFreeString(&KerbGlobalDnsDomainName);
    KerbFreeString(&KerbGlobalMachineName);
    KerbFreeString((PUNICODE_STRING) &KerbGlobalKerbMachineName);
    KerbFreeString(&KerbGlobalMachineServiceName);
    KerbFreeKdcName(&KerbGlobalInternalMachineServiceName);
    KerbFreeKdcName(&KerbGlobalMitMachineServiceName);

    KerbCleanupTicketHandling();
#ifndef WIN32_CHICAGO
    if( KerbGlobalPolicyHandle != NULL )
    {
        LsarClose( &KerbGlobalPolicyHandle );
        KerbGlobalPolicyHandle = NULL;
    }

    if (KerbGlobalDomainSid != NULL)
    {
        KerbFree(KerbGlobalDomainSid);
    }
#endif // WIN32_CHICAGO

    if (KerbGlobalSocketsInitialized)
    {
        KerbCleanupSockets();
    }
    KerbCleanupBindingCache(FALSE);
    KerbUninitializeMitRealmList();
#ifndef WIN32_CHICAGO
    KerbFreeKdcData();
//    RtlDeleteResource(&KerberosGlobalResource);
#endif // WIN32_CHICGAO

    KerbShutdownEvents();
    return(STATUS_SUCCESS);
}

//+-------------------------------------------------------------------------
//
//  Function:   SpShutdown
//
//  Synopsis:   Exported function to shutdown the Kerberos package.
//
//  Effects:    Forces the freeing of all credentials, contexts and
//              logon sessions and frees all global data
//
//  Arguments:  none
//
//  Requires:
//
//  Returns:
//
//  Notes:      STATUS_SUCCESS in all cases
//
//
//--------------------------------------------------------------------------


NTSTATUS NTAPI
SpShutdown(
    VOID
    )
{
#if 0
    SpCleanup();
#endif
    return(STATUS_SUCCESS);
}


#ifndef WIN32_CHICAGO
//+-------------------------------------------------------------------------
//
//  Function:   SpGetInfo
//
//  Synopsis:   Returns information about the package
//
//  Effects:    returns pointers to global data
//
//  Arguments:  PackageInfo - Receives kerberos package information
//
//  Requires:
//
//  Returns:    STATUS_SUCCESS in all cases
//
//  Notes:
//
//
//--------------------------------------------------------------------------


NTSTATUS NTAPI
SpGetInfo(
    OUT PSecPkgInfo PackageInfo
    )
{
    PackageInfo->wVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION;
    PackageInfo->wRPCID = RPC_C_AUTHN_GSS_KERBEROS;
    PackageInfo->fCapabilities = KERBEROS_CAPABILITIES;
    PackageInfo->cbMaxToken       = KerbGlobalMaxTokenSize;
    PackageInfo->Name             = KERBEROS_PACKAGE_NAME;
    PackageInfo->Comment          = KERBEROS_PACKAGE_COMMENT;
    return(STATUS_SUCCESS);
}



//+-------------------------------------------------------------------------
//
//  Function:   SpGetExtendedInformation
//
//  Synopsis:   returns additional information about the package
//
//  Effects:
//
//  Arguments:
//
//  Requires:
//
//  Returns:
//
//  Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS
SpGetExtendedInformation(
    IN  SECPKG_EXTENDED_INFORMATION_CLASS Class,
    OUT PSECPKG_EXTENDED_INFORMATION * ppInformation
    )
{
    NTSTATUS Status = STATUS_SUCCESS;
    PSECPKG_EXTENDED_INFORMATION Information = NULL ;
    PSECPKG_SERIALIZED_OID SerializedOid;
    ULONG Size ;

    switch(Class) {
    case SecpkgGssInfo:
        DsysAssert(gss_mech_krb5_new->length >= 2);
        DsysAssert(gss_mech_krb5_new->length < 127);

        //
        // We need to leave space for the oid and the BER header, which is
        // 0x6 and then the length of the oid.
        //

        Information = (PSECPKG_EXTENDED_INFORMATION)
                            KerbAllocate(sizeof(SECPKG_EXTENDED_INFORMATION) +
                            gss_mech_krb5_new->length - 2);
        if (Information == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto Cleanup;
        }

        Information->Class = SecpkgGssInfo;
        Information->Info.GssInfo.EncodedIdLength = gss_mech_krb5_new->length + 2;
        Information->Info.GssInfo.EncodedId[0] = 0x6;   // BER OID type
        Information->Info.GssInfo.EncodedId[1] = (UCHAR) gss_mech_krb5_new->length;
        RtlCopyMemory(
            &Information->Info.GssInfo.EncodedId[2],
            gss_mech_krb5_new->elements,
            gss_mech_krb5_new->length
            );

            *ppInformation = Information;
            Information = NULL;
        break;
    case SecpkgContextThunks:
        //
        // Note - we don't need to add any space for the thunks as there
        // is only one, and the structure has space for one. If any more
        // thunks are added, we will need to add space for those.
        //

        Information = (PSECPKG_EXTENDED_INFORMATION)
                            KerbAllocate(sizeof(SECPKG_EXTENDED_INFORMATION));
        if (Information == NULL)
        {
            Status = STATUS_INSUFFICIENT_RESOURCES;
            goto Cleanup;
        }
        Information->Class = SecpkgContextThunks;
        Information->Info.ContextThunks.InfoLevelCount = 1;
        Information->Info.ContextThunks.Levels[0] = SECPKG_ATTR_NATIVE_NAMES;
        *ppInformation = Information;
        Information = NULL;
        break;

    case SecpkgWowClientDll:

        //
        // This indicates that we're smart enough to handle wow client processes
        //

        Information = (PSECPKG_EXTENDED_INFORMATION)
                            KerbAllocate( sizeof( SECPKG_EXTENDED_INFORMATION ) +
                                          (MAX_PATH * sizeof(WCHAR) ) );

        if ( Information == NULL )
        {
            Status = STATUS_INSUFFICIENT_RESOURCES ;
            goto Cleanup ;
        }

        Information->Class = SecpkgWowClientDll ;
        Information->Info.WowClientDll.WowClientDllPath.Buffer = (PWSTR) (Information + 1);
        Size = ExpandEnvironmentStrings(
                    L"%SystemRoot%\\" WOW64_SYSTEM_DIRECTORY_U L"\\Kerberos.DLL",
                    Information->Info.WowClientDll.WowClientDllPath.Buffer,
                    MAX_PATH );
        Information->Info.WowClientDll.WowClientDllPath.Length = (USHORT) (Size * sizeof(WCHAR));
        Information->Info.WowClientDll.WowClientDllPath.MaximumLength = (USHORT) ((Size + 1) * sizeof(WCHAR) );
        *ppInformation = Information ;
        Information = NULL ;

        break;

    case SecpkgExtraOids:
        Size = sizeof( SECPKG_EXTENDED_INFORMATION ) +
                2 * sizeof( SECPKG_SERIALIZED_OID ) ;

        Information = (PSECPKG_EXTENDED_INFORMATION)
                            KerbAllocate( Size );


        if ( Information == NULL )
        {
            Status = STATUS_INSUFFICIENT_RESOURCES ;
            goto Cleanup ;
        }
        Information->Class = SecpkgExtraOids ;
        Information->Info.ExtraOids.OidCount = 2 ;

        SerializedOid = Information->Info.ExtraOids.Oids;

        SerializedOid->OidLength = gss_mech_krb5_spnego->length + 2;
        SerializedOid->OidAttributes = SECPKG_CRED_BOTH ;
        SerializedOid->OidValue[ 0 ] = 0x06 ; // BER OID type
        SerializedOid->OidValue[ 1 ] = (UCHAR) gss_mech_krb5_spnego->length;
        RtlCopyMemory(
            &SerializedOid->OidValue[2],
            gss_mech_krb5_spnego->elements,
            gss_mech_krb5_spnego->length
            );

        SerializedOid++ ;

        SerializedOid->OidLength = gss_mech_krb5_u2u->length + 2;
        SerializedOid->OidAttributes = SECPKG_CRED_INBOUND ;
        SerializedOid->OidValue[ 0 ] = 0x06 ; // BER OID type
        SerializedOid->OidValue[ 1 ] = (UCHAR) gss_mech_krb5_u2u->length;
        RtlCopyMemory(
            &SerializedOid->OidValue[2],
            gss_mech_krb5_u2u->elements,
            gss_mech_krb5_u2u->length
            );

        *ppInformation = Information ;
        Information = NULL ;
        break;

    default:
        return(STATUS_INVALID_INFO_CLASS);
    }
Cleanup:
    if (Information != NULL)
    {
        KerbFree(Information);
    }
    return(Status);
}



//+-------------------------------------------------------------------------
//
//  Function:   LsaApInitializePackage
//
//  Synopsis:   Obsolete pacakge initialize function, supported for
//              compatibility only. This function has no effect.
//
//  Effects:    none
//
//  Arguments:
//
//  Requires:
//
//  Returns:    STATUS_SUCCESS always
//
//  Notes:
//
//
//--------------------------------------------------------------------------


NTSTATUS NTAPI
LsaApInitializePackage(
    IN ULONG AuthenticationPackageId,
    IN PLSA_DISPATCH_TABLE LsaDispatchTable,
    IN PLSA_STRING Database OPTIONAL,
    IN PLSA_STRING Confidentiality OPTIONAL,
    OUT PLSA_STRING *AuthenticationPackageName
    )
{
    return(STATUS_SUCCESS);
}

BOOLEAN
KerbIsInitialized(
    VOID
    )
{
    return KerbGlobalInitialized;
}

NTSTATUS
KerbKdcCallBack(
    VOID
    )
{
    PKERB_BINDING_CACHE_ENTRY CacheEntry = NULL;
    NTSTATUS Status = STATUS_SUCCESS;

    KerbGlobalWriteLock();

    KerbGlobalRole = KerbRoleDomainController;

    Status = KerbLoadKdc();


    //
    // Purge the binding cache of entries for this domain
    //

    CacheEntry = KerbLocateBindingCacheEntry(
                    &KerbGlobalDnsDomainName,
                    0,
                    TRUE
                    );
    if (CacheEntry != NULL)
    {
        KerbDereferenceBindingCacheEntry(CacheEntry);
    }
    CacheEntry = KerbLocateBindingCacheEntry(
                    &KerbGlobalDomainName,
                    0,
                    TRUE
                    );
    if (CacheEntry != NULL)
    {
        KerbDereferenceBindingCacheEntry(CacheEntry);
    }

    //
    // PurgeSpnCache, because we may now have "better" state,
    // e.g. right after DCPromo our SPNs may not have replicated.
    //
    KerbCleanupSpnCache();


    KerbGlobalReleaseLock();

    return Status;
}

#endif // WIN32_CHICAGO