You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2191 lines
53 KiB
2191 lines
53 KiB
//+-----------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (c) Microsoft Corporation 1991 - 1992
|
|
//
|
|
// File: KDC.CXX
|
|
//
|
|
// Contents: Base part of the KDC. Global vars, main functions, init
|
|
//
|
|
//
|
|
// History:
|
|
//
|
|
//------------------------------------------------------------------------
|
|
|
|
#include "kdcsvr.hxx"
|
|
|
|
extern "C" {
|
|
#include <lmserver.h>
|
|
#include <srvann.h>
|
|
#include <nlrepl.h>
|
|
#include <dsgetdc.h>
|
|
}
|
|
#include "rpcif.h"
|
|
#include "sockutil.h"
|
|
#include "kdctrace.h"
|
|
#include "fileno.h"
|
|
#define FILENO FILENO_KDC
|
|
|
|
|
|
VOID
|
|
KdcPolicyChangeCallBack(
|
|
IN POLICY_NOTIFICATION_INFORMATION_CLASS MonitorInfoClass
|
|
);
|
|
|
|
VOID
|
|
KdcMyStoreWaitHandler(
|
|
PVOID pVoid,
|
|
BOOLEAN fTimeout
|
|
);
|
|
|
|
//
|
|
// Global data
|
|
//
|
|
|
|
KDC_STATE KdcState = Stopped; // used to signal when
|
|
// authenticated RPC is
|
|
// ready to use - e.g.
|
|
// spmgr has found the
|
|
// kdc
|
|
SERVICE_STATUS_HANDLE hService;
|
|
SERVICE_STATUS SStatus;
|
|
UNICODE_STRING GlobalDomainName;
|
|
UNICODE_STRING GlobalKerberosName;
|
|
UNICODE_STRING GlobalKdcName;
|
|
PKERB_INTERNAL_NAME GlobalKpasswdName = NULL;
|
|
|
|
//
|
|
// Sids used to build other sids quickly.
|
|
//
|
|
PSID GlobalDomainSid;
|
|
PSID GlobalBuiltInSid;
|
|
PSID GlobalEveryoneSid;
|
|
PSID GlobalAuthenticatedUserSid;
|
|
PSID GlobalThisOrganizationSid;
|
|
PSID GlobalOtherOrganizationSid;
|
|
|
|
LSAPR_HANDLE GlobalPolicyHandle = NULL;
|
|
SAMPR_HANDLE GlobalAccountDomainHandle = NULL;
|
|
SAMPR_HANDLE GlobalBuiltInDomainHandle = NULL;
|
|
|
|
BYTE GlobalLocalhostAddress[4];
|
|
HANDLE KdcGlobalDsPausedWaitHandle = NULL;
|
|
HANDLE KdcGlobalDsEventHandle = NULL;
|
|
BOOL KdcGlobalAvoidPdcOnWan = FALSE;
|
|
BOOL KdcGlobalGlobalSafeBoot = FALSE;
|
|
#if DBG
|
|
LARGE_INTEGER tsIn,tsOut;
|
|
#endif
|
|
|
|
// Global KDC values - can be overridden by registry vlaues
|
|
const DWORD KdcUseClientAddressesDefault = FALSE;
|
|
const DWORD KdcDontCheckAddressesDefault = TRUE;
|
|
const DWORD KdcNewConnectionTimeoutDefault = 10;
|
|
const DWORD KdcExistingConnectionTimeoutDefault = 50;
|
|
const DWORD KdcMaxDatagramReplySizeDefault = KERB_MAX_DATAGRAM_REPLY_SIZE;
|
|
const DWORD KdcExtraLogLevelDefault = LOG_DEFAULT;
|
|
const DWORD KdcIssueForwardedTicketsDefault = TRUE;
|
|
|
|
#if DBG
|
|
const ULONG KdcInfoLevelDefault = DEB_ERROR;
|
|
#else
|
|
const ULONG KdcInfoLevelDefault = 0;
|
|
#endif
|
|
|
|
|
|
|
|
DWORD KdcIssueForwardedTickets = KdcIssueForwardedTicketsDefault;
|
|
DWORD KdcUseClientAddresses = KdcUseClientAddressesDefault;
|
|
DWORD KdcDontCheckAddresses = KdcDontCheckAddressesDefault;
|
|
DWORD KdcNewConnectionTimeout = KdcNewConnectionTimeoutDefault;
|
|
DWORD KdcExistingConnectionTimeout = KdcExistingConnectionTimeoutDefault;
|
|
DWORD KdcGlobalMaxDatagramReplySize = KdcMaxDatagramReplySizeDefault;
|
|
DWORD KdcExtraLogLevel = KdcExtraLogLevelDefault;
|
|
|
|
HANDLE hKdcHandles[MAX_KDC_HANDLE] = {0};
|
|
|
|
// This keeps a registry key handle to the HKLM\System\CCSet\Services\Kdc key
|
|
HKEY hKdcParams = NULL;
|
|
|
|
#ifdef ROGUE_DC
|
|
HKEY hKdcRogueKey = NULL;
|
|
#endif
|
|
|
|
HANDLE hKdcWait = NULL;
|
|
|
|
//
|
|
// Prototypes
|
|
//
|
|
|
|
CRITICAL_SECTION ApiCriticalSection;
|
|
ULONG CurrentApiCallers;
|
|
|
|
//
|
|
// MIDL_xxx wrappers to make safealloc happy
|
|
//
|
|
|
|
//
|
|
// kdc preferred crypt list
|
|
//
|
|
|
|
PKERB_CRYPT_LIST kdc_pPreferredCryptList = NULL;
|
|
PKERB_CRYPT_LIST kdc_pMitPrincipalPreferredCryptList = NULL;
|
|
|
|
PVOID
|
|
KdcAllocate(SIZE_T size)
|
|
{
|
|
return MIDL_user_allocate(size);
|
|
}
|
|
|
|
VOID
|
|
KdcFree(PVOID buff)
|
|
{
|
|
MIDL_user_free(buff);
|
|
}
|
|
|
|
AUTHZ_RESOURCE_MANAGER_HANDLE KdcAuthzRM = NULL;
|
|
|
|
//--------------------------------------------------------------------
|
|
//
|
|
// Name: KdcComputeAuthzGroups
|
|
//
|
|
// Synopsis: Authz callback for add groups to authz client context
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//---
|
|
|
|
BOOL
|
|
KdcComputeAuthzGroups(
|
|
IN AUTHZ_CLIENT_CONTEXT_HANDLE hAuthzClientContext,
|
|
IN PVOID Args,
|
|
OUT PSID_AND_ATTRIBUTES *pSidAttrArray,
|
|
OUT PDWORD pSidCount,
|
|
OUT PSID_AND_ATTRIBUTES *pRestrictedSidAttrArray,
|
|
OUT PDWORD pRestrictedSidCount
|
|
)
|
|
{
|
|
|
|
PKDC_AUTHZ_INFO AuthzInfo = (PKDC_AUTHZ_INFO) Args;
|
|
|
|
*pSidAttrArray = (PSID_AND_ATTRIBUTES) AuthzInfo->SidAndAttributes;
|
|
*pSidCount = AuthzInfo->SidCount;
|
|
*pRestrictedSidAttrArray = NULL;
|
|
*pRestrictedSidCount = 0;
|
|
|
|
return (TRUE);
|
|
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
//
|
|
// Name: KdcFreeAuthzGroups
|
|
//
|
|
// Synopsis: Basically a no-op, as we already have a copy of the
|
|
// sids
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
//---
|
|
|
|
VOID
|
|
KdcFreeAuthzGroups(IN PSID_AND_ATTRIBUTES pSidAttrArray)
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------
|
|
//
|
|
// Name: KdcInitializeAuthzRM
|
|
//
|
|
// Synopsis: Validate that S4U caller has access to expand groups
|
|
//
|
|
// Effects: Use Authz to check client context.
|
|
//
|
|
// Arguments: S4UClientName - ClientName from S4U PA Data
|
|
// PAC - Resultant PAC (signed w/? key)
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns: KDC_ERR_ or KRB_AP_ERR errors only
|
|
//
|
|
// Notes: Free client name and realm w/
|
|
//
|
|
//
|
|
//---
|
|
|
|
NTSTATUS
|
|
KdcInitializeAuthzRM()
|
|
{
|
|
|
|
if (!AuthzInitializeResourceManager(
|
|
0,
|
|
NULL,
|
|
KdcComputeAuthzGroups,
|
|
KdcFreeAuthzGroups,
|
|
L"KDC",
|
|
&KdcAuthzRM
|
|
))
|
|
{
|
|
DebugLog((DEB_ERROR, "AuthzInitializeRm failed %x\n", GetLastError()));
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
KdcFreeAuthzRm()
|
|
{
|
|
if (!AuthzFreeResourceManager(KdcAuthzRM))
|
|
{
|
|
DebugLog((DEB_ERROR, "AuthzFreeResourceManager failed %x\n", GetLastError()));
|
|
}
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: UpdateStatus
|
|
//
|
|
// Synopsis: Updates the KDC's service status with the service controller
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// History:
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
UpdateStatus(DWORD dwState)
|
|
{
|
|
TRACE(KDC, UpdateStatus, DEB_FUNCTION);
|
|
|
|
SStatus.dwCurrentState = dwState;
|
|
if ((dwState == SERVICE_START_PENDING) || (dwState == SERVICE_STOP_PENDING))
|
|
{
|
|
SStatus.dwCheckPoint++;
|
|
SStatus.dwWaitHint = 10000;
|
|
}
|
|
else
|
|
{
|
|
SStatus.dwCheckPoint = 0;
|
|
SStatus.dwWaitHint = 0;
|
|
}
|
|
|
|
if (!SetServiceStatus(hService, &SStatus)) {
|
|
DebugLog((DEB_ERROR,"(%x)Failed to set service status: %d\n",GetLastError()));
|
|
return(FALSE);
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Handler
|
|
//
|
|
// Synopsis: Process and respond to a control signal from the service
|
|
// controller.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// History:
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
Handler(DWORD dwControl)
|
|
{
|
|
TRACE(KDC, Handler, DEB_FUNCTION);
|
|
|
|
switch (dwControl)
|
|
{
|
|
|
|
case SERVICE_CONTROL_STOP:
|
|
ShutDown( L"Service" );
|
|
break;
|
|
|
|
default:
|
|
D_DebugLog((DEB_WARN, "Ignoring SC message %d\n",dwControl));
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
KdcWaitForSamService(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure waits for the SAM service to start and to complete
|
|
all its initialization.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the SAM service is successfully starts.
|
|
|
|
FALSE : if the SAM service can't start.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
DWORD WaitStatus;
|
|
UNICODE_STRING EventName;
|
|
HANDLE EventHandle;
|
|
OBJECT_ATTRIBUTES EventAttributes;
|
|
|
|
//
|
|
// open SAM event
|
|
//
|
|
|
|
RtlInitUnicodeString( &EventName, L"\\SAM_SERVICE_STARTED");
|
|
InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL );
|
|
|
|
Status = NtOpenEvent( &EventHandle,
|
|
SYNCHRONIZE|EVENT_MODIFY_STATE,
|
|
&EventAttributes );
|
|
|
|
if ( !NT_SUCCESS(Status)) {
|
|
|
|
if( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
|
|
|
|
//
|
|
// SAM hasn't created this event yet, let us create it now.
|
|
// SAM opens this event to set it.
|
|
//
|
|
|
|
Status = NtCreateEvent(
|
|
&EventHandle,
|
|
SYNCHRONIZE|EVENT_MODIFY_STATE,
|
|
&EventAttributes,
|
|
NotificationEvent,
|
|
FALSE // The event is initially not signaled
|
|
);
|
|
|
|
if( Status == STATUS_OBJECT_NAME_EXISTS ||
|
|
Status == STATUS_OBJECT_NAME_COLLISION ) {
|
|
|
|
//
|
|
// second chance, if the SAM created the event before we
|
|
// do.
|
|
//
|
|
|
|
Status = NtOpenEvent( &EventHandle,
|
|
SYNCHRONIZE|EVENT_MODIFY_STATE,
|
|
&EventAttributes );
|
|
}
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// could not make the event handle
|
|
//
|
|
|
|
DebugLog((DEB_ERROR,
|
|
"KdcWaitForSamService couldn't make the event handle : "
|
|
"%lx\n", Status));
|
|
|
|
return( FALSE );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop waiting.
|
|
//
|
|
|
|
for (;;) {
|
|
WaitStatus = WaitForSingleObject( EventHandle,
|
|
5*1000 ); // 5 Seconds
|
|
|
|
if ( WaitStatus == WAIT_TIMEOUT ) {
|
|
DebugLog((DEB_WARN,
|
|
"KdcWaitForSamService 5-second timeout (Rewaiting)\n" ));
|
|
if (!UpdateStatus( SERVICE_START_PENDING )) {
|
|
(VOID) NtClose( EventHandle );
|
|
return FALSE;
|
|
}
|
|
continue;
|
|
|
|
} else if ( WaitStatus == WAIT_OBJECT_0 ) {
|
|
break;
|
|
|
|
} else {
|
|
DebugLog((DEB_ERROR,
|
|
"KdcWaitForSamService: error %ld %ld\n",
|
|
GetLastError(),
|
|
WaitStatus ));
|
|
(VOID) NtClose( EventHandle );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
(VOID) NtClose( EventHandle );
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
KdcDsNotPaused(
|
|
IN PVOID Context,
|
|
IN BOOLEAN TimedOut
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Worker routine that gets called when the DS is no longer paused.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Tell the kerberos client that we are a DC.
|
|
//
|
|
|
|
NtStatus = KerbKdcCallBack();
|
|
if ( !NT_SUCCESS(NtStatus) )
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Can't tell Kerberos that we're a DC 0x%x\n", NtStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
NtStatus = I_NetLogonSetServiceBits( DS_KDC_FLAG, DS_KDC_FLAG );
|
|
|
|
if ( !NT_SUCCESS(NtStatus) )
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Can't tell netlogon we're started 0x%x\n", NtStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
D_DebugLog((DEB_TRACE,"Ds is no longer paused\n"));
|
|
|
|
Cleanup:
|
|
|
|
UNREFERENCED_PARAMETER( Context );
|
|
UNREFERENCED_PARAMETER( TimedOut );
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
KdcRegisterToWaitForDS(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This procedure registers to wait for the DS to start and to complete
|
|
all its initialization. The main reason we do this is because we don't
|
|
want to start doing kerberos authentications because we don't know that
|
|
the DS has the latest db. It needs to check with all the existing Dc's
|
|
out there to see if there's any db merges to be done. When the
|
|
DS_SYNCED_EVENT_NAME is set, we're ready.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the register to wait succeeded
|
|
|
|
FALSE : if the register to wait didn't succeed.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN fRet = FALSE;
|
|
|
|
//
|
|
// open the DS event
|
|
//
|
|
|
|
KdcGlobalDsEventHandle = OpenEvent( SYNCHRONIZE,
|
|
FALSE,
|
|
DS_SYNCED_EVENT_NAME_W);
|
|
|
|
if ( KdcGlobalDsEventHandle == NULL)
|
|
{
|
|
//
|
|
// could not open the event handle
|
|
//
|
|
|
|
D_DebugLog((DEB_ERROR,"KdcRegisterToWaitForDS couldn't open the event handle\n"));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( !RegisterWaitForSingleObject(
|
|
&KdcGlobalDsPausedWaitHandle,
|
|
KdcGlobalDsEventHandle,
|
|
KdcDsNotPaused, // Callback routine
|
|
NULL, // No context
|
|
INFINITE, // Wait forever
|
|
WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE ) )
|
|
{
|
|
D_DebugLog((DEB_ERROR, "KdcRegisterToWaitForDS: Cannot register for DS Synced callback 0x%x\n", GetLastError()));
|
|
goto Cleanup;
|
|
}
|
|
|
|
fRet = TRUE;
|
|
|
|
Cleanup:
|
|
return fRet;
|
|
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KdcOpenEvent
|
|
//
|
|
// Synopsis: Just like the Win32 function, except that it allows
|
|
// for names at the root of the namespace.
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes: The code was copied from private\windows\base\client\synch.c
|
|
// and the base directory was changed to NULL
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
HANDLE
|
|
APIENTRY
|
|
KdcOpenEvent(
|
|
DWORD DesiredAccess,
|
|
BOOL bInheritHandle,
|
|
LPWSTR lpName
|
|
)
|
|
{
|
|
TRACE(KDC, KdcOpenEvent, DEB_FUNCTION);
|
|
|
|
OBJECT_ATTRIBUTES Obja;
|
|
UNICODE_STRING ObjectName;
|
|
NTSTATUS Status;
|
|
HANDLE Object;
|
|
|
|
if ( !lpName ) {
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return NULL;
|
|
}
|
|
RtlInitUnicodeString(&ObjectName,lpName);
|
|
|
|
InitializeObjectAttributes(
|
|
&Obja,
|
|
&ObjectName,
|
|
(bInheritHandle ? OBJ_INHERIT : 0),
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtCreateEvent(
|
|
&Object,
|
|
DesiredAccess,
|
|
&Obja,
|
|
NotificationEvent,
|
|
(BOOLEAN) FALSE // The event is initially not signaled
|
|
);
|
|
|
|
if ( !NT_SUCCESS(Status)) {
|
|
|
|
//
|
|
// If the event already exists, the server beat us to creating it.
|
|
// Just open it.
|
|
//
|
|
|
|
if( Status == STATUS_OBJECT_NAME_EXISTS ||
|
|
Status == STATUS_OBJECT_NAME_COLLISION ) {
|
|
|
|
Status = NtOpenEvent( &Object,
|
|
DesiredAccess,
|
|
&Obja );
|
|
}
|
|
}
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
SetLastError(RtlNtStatusToDosError( Status ));
|
|
return NULL;
|
|
}
|
|
|
|
return Object;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KdcStartEvent
|
|
//
|
|
// Synopsis: sets the KdcStartEvent
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes: The SPMgr must have created this event before this
|
|
// is called
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
void
|
|
SetKdcStartEvent()
|
|
{
|
|
TRACE(KDC, SetKdcStartEvent, DEB_FUNCTION);
|
|
|
|
HANDLE hEvent;
|
|
hEvent = KdcOpenEvent(EVENT_MODIFY_STATE,FALSE,KDC_START_EVENT);
|
|
if (hEvent != NULL)
|
|
{
|
|
SetEvent(hEvent);
|
|
CloseHandle(hEvent);
|
|
D_DebugLog((DEB_TRACE,"Set event %ws\n",KDC_START_EVENT));
|
|
}
|
|
else
|
|
{
|
|
DWORD dw = GetLastError();
|
|
if (dw != ERROR_FILE_NOT_FOUND)
|
|
DebugLog((DEB_ERROR,"Error opening %ws: %d\n",KDC_START_EVENT,dw));
|
|
else
|
|
D_DebugLog((DEB_TRACE,"Error opening %ws: %d\n",KDC_START_EVENT,dw));
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KdcLoadParameters
|
|
//
|
|
// Synopsis: Loads random parameters from registry
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
KdcLoadParameters(
|
|
VOID
|
|
)
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
// LPNET_CONFIG_HANDLE ConfigHandle = NULL;
|
|
LPNET_CONFIG_HANDLE NetlogonInfo = NULL;
|
|
|
|
int err = 0;
|
|
HKEY Key ;
|
|
|
|
//
|
|
// Uncomment the below when there's something we need to read from this key
|
|
//
|
|
|
|
// NetStatus = NetpOpenConfigData(
|
|
// &ConfigHandle,
|
|
// NULL, // noserer name
|
|
// SERVICE_KDC,
|
|
// TRUE // read only
|
|
// );
|
|
// if (NetStatus != NO_ERROR)
|
|
// {
|
|
// // we could return, but then we'd lose data
|
|
// D_DebugLog((DEB_WARN, "Couldn't open KDC config data - %x\n", NetStatus));
|
|
// // return;
|
|
// }
|
|
|
|
//
|
|
// Open Netlogon service key for AvoidPdcOnWan
|
|
//
|
|
NetStatus = NetpOpenConfigData(
|
|
&NetlogonInfo,
|
|
NULL,
|
|
SERVICE_NETLOGON,
|
|
TRUE
|
|
);
|
|
|
|
if (NetStatus != NO_ERROR)
|
|
{
|
|
D_DebugLog((DEB_WARN, "Failed to open netlogon key - %x\n", NetStatus));
|
|
return;
|
|
}
|
|
|
|
NetStatus = NetpGetConfigBool(
|
|
NetlogonInfo,
|
|
L"AvoidPdcOnWan",
|
|
FALSE,
|
|
&KdcGlobalAvoidPdcOnWan
|
|
);
|
|
|
|
if (NetStatus != NO_ERROR)
|
|
{
|
|
D_DebugLog((DEB_WARN, "Failed to read netlogon config value KdcGlobalAvoidPdcOnWan - %x\n", NetStatus));
|
|
}
|
|
|
|
// NetpCloseConfigData( ConfigHandle );
|
|
NetpCloseConfigData( NetlogonInfo );
|
|
|
|
//
|
|
// Check for safeboot mode
|
|
//
|
|
|
|
err = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Control\\SafeBoot\\Option",
|
|
0,
|
|
KEY_READ,
|
|
&Key );
|
|
|
|
if ( err == ERROR_SUCCESS )
|
|
{
|
|
ULONG Value = 0 ;
|
|
ULONG Size = sizeof( ULONG );
|
|
ULONG Type = 0;
|
|
|
|
err = RegQueryValueExW(
|
|
Key,
|
|
L"OptionValue",
|
|
0,
|
|
&Type,
|
|
(PUCHAR) &Value,
|
|
&Size );
|
|
|
|
RegCloseKey( Key );
|
|
|
|
if ( err == ERROR_SUCCESS )
|
|
{
|
|
if (Value)
|
|
{
|
|
KdcGlobalGlobalSafeBoot = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
typedef struct _KDC_REG_PARAMETER {
|
|
|
|
WCHAR * Name;
|
|
DWORD * Address;
|
|
DWORD DefaultValue;
|
|
BOOL ReverseSense; // 0 means TRUE
|
|
BOOL Dynamic;
|
|
|
|
} KDC_REG_PARAMETER, *PKDC_REG_PARAMETER;
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: GetRegistryDwords
|
|
//
|
|
// Synopsis: Gets a set of DWORDs from the registry
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments: [hPrimaryKey] -- Key to start from
|
|
// [pszKey] -- Name of the subkey
|
|
// [Count] -- Number of values
|
|
// [Values] -- returned values
|
|
//
|
|
// Returns: Nothing (Default values assumed on error)
|
|
//
|
|
// History: 3-31-93 RichardW Created
|
|
// 10-14-01 MarkPu Enabled multiple values
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
GetRegistryDwords(
|
|
IN HKEY hKey,
|
|
IN ULONG Count,
|
|
IN PKDC_REG_PARAMETER Values
|
|
)
|
|
{
|
|
static BOOL ValuesInitialized = FALSE;
|
|
|
|
while ( Count-- > 0 ) {
|
|
|
|
LONG err;
|
|
DWORD dwType;
|
|
DWORD dwValue;
|
|
DWORD cbDword = sizeof(DWORD);
|
|
|
|
if ( Values[Count].Dynamic == FALSE &&
|
|
ValuesInitialized )
|
|
{
|
|
//
|
|
// Only initialize static values once!!!
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
err = RegQueryValueEx(
|
|
hKey,
|
|
Values[Count].Name,
|
|
NULL,
|
|
&dwType,
|
|
(PBYTE) &dwValue,
|
|
&cbDword
|
|
);
|
|
|
|
if ( err || ( dwType != REG_DWORD )) {
|
|
|
|
*Values[Count].Address = Values[Count].DefaultValue;
|
|
|
|
} else if ( Values[Count].ReverseSense ) {
|
|
|
|
*Values[Count].Address = !dwValue;
|
|
|
|
} else {
|
|
|
|
*Values[Count].Address = dwValue;
|
|
}
|
|
}
|
|
|
|
ValuesInitialized = TRUE;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: KerbGetKdcRegParams
|
|
//
|
|
// Synopsis: Gets the debug paramaters from the registry
|
|
//
|
|
// Arguments: HKEY to HKLM/System/CCS/Services/Kdc
|
|
//
|
|
// Notes: Sets KDCInfolevel for debug spew
|
|
//
|
|
void
|
|
KerbGetKdcRegParams(HKEY ParamKey)
|
|
{
|
|
//
|
|
// NOTE: UDP datagram size is NOT dynamic; changing it requires restaring
|
|
// the KDC. The reason is that this is the value that we initialize ATQ
|
|
// with and ATQ can't be re-initialized underneath a running KDC.
|
|
// See bug #645637 for details.
|
|
//
|
|
|
|
KDC_REG_PARAMETER Parameters[] =
|
|
{
|
|
{ L"KdcUseClientAddresses", &KdcUseClientAddresses, KdcUseClientAddressesDefault, FALSE, TRUE },
|
|
{ L"KdcDontCheckAddresses", &KdcDontCheckAddresses, KdcDontCheckAddressesDefault, FALSE, TRUE },
|
|
{ L"NewConnectionTimeout", &KdcNewConnectionTimeout, KdcNewConnectionTimeoutDefault, FALSE, TRUE },
|
|
{ L"ExistingConnectionTimeout", &KdcExistingConnectionTimeout, KdcExistingConnectionTimeoutDefault, FALSE, TRUE },
|
|
{ L"MaxDatagramReplySize", &KdcGlobalMaxDatagramReplySize, KdcMaxDatagramReplySizeDefault, FALSE, FALSE },
|
|
{ L"KdcExtraLogLevel", &KdcExtraLogLevel, KdcExtraLogLevelDefault, FALSE, TRUE },
|
|
{ L"KdcDebugLevel", &KDCInfoLevel, KdcInfoLevelDefault, FALSE, TRUE },
|
|
{ L"KdcIssueForwardedTickets", &KdcIssueForwardedTickets, KdcIssueForwardedTicketsDefault, FALSE, TRUE }
|
|
};
|
|
|
|
//
|
|
// Get the DWORD parameters that can change during a boot.
|
|
//
|
|
|
|
GetRegistryDwords(
|
|
ParamKey,
|
|
sizeof( Parameters ) / sizeof( Parameters[0]),
|
|
Parameters
|
|
);
|
|
|
|
//
|
|
// BUG 676343
|
|
// UDP packets have a 2-byte length field so under no
|
|
// circumstances can they be bigger than 64K
|
|
//
|
|
|
|
KdcGlobalMaxDatagramReplySize = min( KdcGlobalMaxDatagramReplySize, 64 * 1024 );
|
|
|
|
//
|
|
// Fixup the common2 debug level.
|
|
//
|
|
|
|
KSuppInfoLevel = KDCInfoLevel;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: KerbWatchKdcParamKey
|
|
//
|
|
// 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
|
|
KerbWatchKdcParamKey(PVOID pCtxt,
|
|
BOOLEAN fWaitStatus)
|
|
{
|
|
|
|
NTSTATUS Status;
|
|
LONG lRes = ERROR_SUCCESS;
|
|
|
|
if (NULL == hKdcParams) // first time we've been called.
|
|
{
|
|
lRes = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services\\Kdc",
|
|
0,
|
|
KEY_READ,
|
|
&hKdcParams
|
|
);
|
|
|
|
if (ERROR_SUCCESS != lRes)
|
|
{
|
|
DebugLog((DEB_WARN,"Failed to open kerberos key: 0x%x\n", lRes));
|
|
goto Reregister;
|
|
}
|
|
}
|
|
|
|
if (NULL != hKdcWait)
|
|
{
|
|
Status = RtlDeregisterWait(hKdcWait);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_WARN, "Failed to Deregister wait on registry key: 0x%x\n", Status));
|
|
goto Reregister;
|
|
}
|
|
|
|
}
|
|
|
|
lRes = RegNotifyChangeKeyValue(
|
|
hKdcParams,
|
|
FALSE,
|
|
REG_NOTIFY_CHANGE_LAST_SET,
|
|
(HANDLE) pCtxt,
|
|
TRUE);
|
|
|
|
if (ERROR_SUCCESS != lRes)
|
|
{
|
|
DebugLog((DEB_ERROR,"Debug RegNotify setup failed: 0x%x\n", lRes));
|
|
// we're tanked now. No further notifications, so get this one
|
|
}
|
|
|
|
KerbGetKdcRegParams(hKdcParams);
|
|
|
|
Reregister:
|
|
|
|
Status = RtlRegisterWait(&hKdcWait,
|
|
(HANDLE) pCtxt,
|
|
KerbWatchKdcParamKey,
|
|
(HANDLE) pCtxt,
|
|
INFINITE,
|
|
WT_EXECUTEONLYONCE);
|
|
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: WaitKdcCleanup
|
|
//
|
|
// Synopsis: Cleans up for KerbWatchKdcParamKey
|
|
//
|
|
// Arguments: <none>
|
|
//
|
|
// Notes: .
|
|
//
|
|
VOID
|
|
WaitKdcCleanup(HANDLE hEvent)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
if (NULL != hKdcWait) {
|
|
Status = RtlDeregisterWait(hKdcWait);
|
|
hKdcWait = NULL;
|
|
}
|
|
|
|
if (NT_SUCCESS(Status) && NULL != hEvent) {
|
|
CloseHandle(hEvent);
|
|
hEvent = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: OpenAccountDomain
|
|
//
|
|
// Synopsis: Opens the account domain and stores a handle to it.
|
|
//
|
|
// Effects: Sets GlobalAccountDomainHandle and GlobalPolicyHandle on
|
|
// success.
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
NTSTATUS
|
|
OpenAccountDomain()
|
|
{
|
|
NTSTATUS Status;
|
|
PLSAPR_POLICY_INFORMATION PolicyInformation = NULL;
|
|
SAMPR_HANDLE ServerHandle = NULL;
|
|
ULONG BuiltInSubAuthority = SECURITY_BUILTIN_DOMAIN_RID;
|
|
ULONG AuthenticatedSubAuthority = SECURITY_AUTHENTICATED_USER_RID;
|
|
ULONG ThisOrganizationSubAuthority = SECURITY_THIS_ORGANIZATION_RID;
|
|
ULONG OtherOrganizationSubAuthority = SECURITY_OTHER_ORGANIZATION_RID;
|
|
|
|
SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
|
|
SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
|
|
|
|
PSID TmpBuiltInSid = NULL;
|
|
|
|
Status = LsaIOpenPolicyTrusted( & GlobalPolicyHandle );
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to open policy trusted: 0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = LsarQueryInformationPolicy(
|
|
GlobalPolicyHandle,
|
|
PolicyAccountDomainInformation,
|
|
&PolicyInformation
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to query information policy: 0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the name and SID out of the account domain information
|
|
//
|
|
|
|
Status = KerbDuplicateString(
|
|
&GlobalDomainName,
|
|
(PUNICODE_STRING) &PolicyInformation->PolicyAccountDomainInfo.DomainName
|
|
);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
GlobalDomainSid = (PSID) LocalAlloc(0, RtlLengthSid(PolicyInformation->PolicyAccountDomainInfo.DomainSid));
|
|
if (GlobalDomainSid == 0)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
RtlCopyMemory(
|
|
GlobalDomainSid,
|
|
PolicyInformation->PolicyAccountDomainInfo.DomainSid,
|
|
RtlLengthSid(PolicyInformation->PolicyAccountDomainInfo.DomainSid)
|
|
);
|
|
|
|
//
|
|
// Connect to SAM and open the account domain
|
|
//
|
|
|
|
Status = SamIConnect(
|
|
NULL, // no server name
|
|
&ServerHandle,
|
|
0, // ignore desired access,
|
|
TRUE // trusted caller
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR,"Failed to connect to SAM: 0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Finally open the account domain.
|
|
//
|
|
|
|
Status = SamrOpenDomain(
|
|
ServerHandle,
|
|
DOMAIN_ALL_ACCESS,
|
|
(PRPC_SID) PolicyInformation->PolicyAccountDomainInfo.DomainSid,
|
|
&GlobalAccountDomainHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR, "Failed to open account domain: 0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Create the built-in group sid, the everyone sid, and
|
|
// the authenticated user sid. These are used to build authz sid lists.
|
|
//
|
|
|
|
|
|
GlobalBuiltInSid = (PSID) LocalAlloc(LMEM_ZEROINIT, RtlLengthRequiredSid(2));
|
|
if (NULL == GlobalBuiltInSid)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitializeSid(
|
|
GlobalBuiltInSid,
|
|
&SidAuthority,
|
|
2
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
RtlSubAuthoritySid(GlobalBuiltInSid,0),
|
|
&BuiltInSubAuthority,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
|
|
|
|
//
|
|
// Everyone sid
|
|
//
|
|
GlobalEveryoneSid = (PSID) LocalAlloc(LMEM_ZEROINIT, RtlLengthRequiredSid(1));
|
|
if (NULL == GlobalEveryoneSid)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitializeSid(
|
|
GlobalEveryoneSid,
|
|
&WorldAuthority,
|
|
1
|
|
);
|
|
|
|
|
|
//
|
|
// Authenticated user sid
|
|
//
|
|
GlobalAuthenticatedUserSid = (PSID) LocalAlloc(LMEM_ZEROINIT, RtlLengthRequiredSid(1));
|
|
if (NULL == GlobalAuthenticatedUserSid)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitializeSid(
|
|
GlobalAuthenticatedUserSid,
|
|
&SidAuthority,
|
|
1
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
RtlSubAuthoritySid(GlobalAuthenticatedUserSid,0),
|
|
&AuthenticatedSubAuthority,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
//
|
|
// "This Organization" SID
|
|
//
|
|
GlobalThisOrganizationSid = (PSID) LocalAlloc(LMEM_ZEROINIT, RtlLengthRequiredSid(1));
|
|
if (NULL == GlobalThisOrganizationSid)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitializeSid(
|
|
GlobalThisOrganizationSid,
|
|
&SidAuthority,
|
|
1
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
RtlSubAuthoritySid(GlobalThisOrganizationSid,0),
|
|
&ThisOrganizationSubAuthority,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
//
|
|
// "Other Organization" SID
|
|
//
|
|
GlobalOtherOrganizationSid = (PSID) LocalAlloc(LMEM_ZEROINIT, RtlLengthRequiredSid(1));
|
|
if (NULL == GlobalOtherOrganizationSid)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitializeSid(
|
|
GlobalOtherOrganizationSid,
|
|
&SidAuthority,
|
|
1
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
RtlSubAuthoritySid(GlobalOtherOrganizationSid,0),
|
|
&OtherOrganizationSubAuthority,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
//
|
|
// Make a temporary sid, w/ only 1 sub authority. The above "GlobalBuiltinSid"
|
|
// is explicitly used to build other builtin sids for S4u checks.
|
|
//
|
|
TmpBuiltInSid = (PSID) LocalAlloc(LMEM_ZEROINIT, RtlLengthRequiredSid(1));
|
|
if (NULL == TmpBuiltInSid)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitializeSid(
|
|
TmpBuiltInSid,
|
|
&SidAuthority,
|
|
1
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
RtlSubAuthoritySid(TmpBuiltInSid,0),
|
|
&BuiltInSubAuthority,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
Status = SamrOpenDomain(
|
|
ServerHandle,
|
|
DOMAIN_ALL_ACCESS,
|
|
(PRPC_SID) TmpBuiltInSid,
|
|
&GlobalBuiltInDomainHandle
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DebugLog((DEB_ERROR, "Failed to open builtin domain: 0x%x\n",Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
if (PolicyInformation != NULL)
|
|
{
|
|
LsaIFree_LSAPR_POLICY_INFORMATION(
|
|
PolicyAccountDomainInformation,
|
|
PolicyInformation
|
|
);
|
|
}
|
|
if (ServerHandle != NULL)
|
|
{
|
|
SamrCloseHandle(&ServerHandle);
|
|
}
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (GlobalPolicyHandle != NULL)
|
|
{
|
|
LsarClose(&GlobalPolicyHandle);
|
|
GlobalPolicyHandle = NULL;
|
|
}
|
|
if (GlobalAccountDomainHandle != NULL)
|
|
{
|
|
SamrCloseHandle(&GlobalAccountDomainHandle);
|
|
GlobalAccountDomainHandle = NULL;
|
|
}
|
|
|
|
if (GlobalBuiltInDomainHandle != NULL)
|
|
{
|
|
SamrCloseHandle(&GlobalBuiltInDomainHandle);
|
|
GlobalBuiltInDomainHandle = NULL;
|
|
}
|
|
|
|
if (GlobalBuiltInSid != NULL)
|
|
{
|
|
LocalFree(GlobalBuiltInSid);
|
|
GlobalBuiltInSid = NULL;
|
|
}
|
|
|
|
if (GlobalEveryoneSid != NULL)
|
|
{
|
|
LocalFree(GlobalEveryoneSid);
|
|
GlobalEveryoneSid = NULL;
|
|
}
|
|
|
|
if (GlobalAuthenticatedUserSid != NULL)
|
|
{
|
|
LocalFree(GlobalAuthenticatedUserSid);
|
|
GlobalAuthenticatedUserSid = NULL;
|
|
}
|
|
|
|
if (GlobalThisOrganizationSid != NULL)
|
|
{
|
|
LocalFree(GlobalThisOrganizationSid);
|
|
GlobalThisOrganizationSid = NULL;
|
|
}
|
|
|
|
if (GlobalOtherOrganizationSid != NULL)
|
|
{
|
|
LocalFree(GlobalOtherOrganizationSid);
|
|
GlobalOtherOrganizationSid = NULL;
|
|
}
|
|
}
|
|
|
|
if (TmpBuiltInSid != NULL)
|
|
{
|
|
LocalFree(TmpBuiltInSid);
|
|
}
|
|
|
|
|
|
return(Status);
|
|
|
|
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: CleanupAccountDomain
|
|
//
|
|
// Synopsis: cleans up resources associated with SAM and LSA
|
|
//
|
|
// Effects:
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Requires:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
CleanupAccountDomain()
|
|
{
|
|
if (GlobalPolicyHandle != NULL)
|
|
{
|
|
LsarClose(&GlobalPolicyHandle);
|
|
GlobalPolicyHandle = NULL;
|
|
}
|
|
|
|
if (GlobalAccountDomainHandle != NULL)
|
|
{
|
|
SamrCloseHandle(&GlobalAccountDomainHandle);
|
|
GlobalAccountDomainHandle = NULL;
|
|
}
|
|
|
|
if ( GlobalBuiltInDomainHandle != NULL )
|
|
{
|
|
SamrCloseHandle(&GlobalBuiltInDomainHandle);
|
|
GlobalBuiltInDomainHandle = NULL;
|
|
}
|
|
|
|
KerbFreeString(&GlobalDomainName);
|
|
|
|
if (GlobalDomainSid != NULL)
|
|
{
|
|
LocalFree(GlobalDomainSid);
|
|
GlobalDomainSid = NULL;
|
|
}
|
|
|
|
if (GlobalBuiltInSid != NULL)
|
|
{
|
|
LocalFree(GlobalBuiltInSid);
|
|
GlobalBuiltInSid = NULL;
|
|
}
|
|
|
|
if (GlobalEveryoneSid != NULL)
|
|
{
|
|
LocalFree(GlobalEveryoneSid);
|
|
GlobalEveryoneSid = NULL;
|
|
}
|
|
|
|
if (GlobalAuthenticatedUserSid != NULL)
|
|
{
|
|
LocalFree(GlobalAuthenticatedUserSid);
|
|
GlobalAuthenticatedUserSid = NULL;
|
|
}
|
|
|
|
if (GlobalThisOrganizationSid != NULL)
|
|
{
|
|
LocalFree(GlobalThisOrganizationSid);
|
|
GlobalThisOrganizationSid = NULL;
|
|
}
|
|
|
|
if (GlobalOtherOrganizationSid != NULL)
|
|
{
|
|
LocalFree(GlobalOtherOrganizationSid);
|
|
GlobalOtherOrganizationSid = NULL;
|
|
}
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: KerbInitPreferredCryptList
|
|
//
|
|
// Synopsis: Build a crypt list, the strongest etype first, used to select
|
|
// etype_for_key(server.key) per rfc 1510
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
KerbInitPreferredCryptList(
|
|
VOID
|
|
)
|
|
{
|
|
KERBERR KerbErr = KRB_ERR_GENERIC;
|
|
|
|
ULONG CryptTypes[KERB_MAX_CRYPTO_SYSTEMS] = {0};
|
|
ULONG CryptTypeCount = 0;
|
|
|
|
CryptTypes[CryptTypeCount++] = KERB_ETYPE_RC4_HMAC_NT;
|
|
CryptTypes[CryptTypeCount++] = KERB_ETYPE_RC4_HMAC_NT_EXP;
|
|
CryptTypes[CryptTypeCount++] = KERB_ETYPE_DES_CBC_MD5_NT;
|
|
CryptTypes[CryptTypeCount++] = KERB_ETYPE_DES_CBC_MD5;
|
|
CryptTypes[CryptTypeCount++] = KERB_ETYPE_DES_CBC_CRC;
|
|
|
|
KerbErr = KerbConvertArrayToCryptList(
|
|
&kdc_pPreferredCryptList,
|
|
CryptTypes,
|
|
CryptTypeCount,
|
|
FALSE
|
|
);
|
|
if (KERB_SUCCESS(KerbErr))
|
|
{
|
|
CryptTypeCount = 0;
|
|
CryptTypes[CryptTypeCount++] = KERB_ETYPE_DES_CBC_MD5;
|
|
CryptTypes[CryptTypeCount++] = KERB_ETYPE_DES_CBC_CRC;
|
|
KerbErr = KerbConvertArrayToCryptList(
|
|
&kdc_pMitPrincipalPreferredCryptList,
|
|
CryptTypes,
|
|
CryptTypeCount,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
return KerbMapKerbError(KerbErr);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Name: KdcServiceMain
|
|
//
|
|
// Synopsis: This is the main KDC thread.
|
|
//
|
|
// Arguments: dwArgc -
|
|
// pszArgv -
|
|
//
|
|
// Notes: This intializes everything, and starts the working threads.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
extern "C"
|
|
void
|
|
KdcServiceMain( DWORD dwArgc,
|
|
LPTSTR *pszArgv)
|
|
{
|
|
UNREFERENCED_PARAMETER(dwArgc);
|
|
UNREFERENCED_PARAMETER(pszArgv);
|
|
|
|
TRACE(KDC, KdcServiceMain, DEB_FUNCTION);
|
|
|
|
ULONG RpcStatus;
|
|
HANDLE hKdcParamEvent = NULL;
|
|
ULONG ulStates = 0;
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
NTSTATUS TempStatus;
|
|
NT_PRODUCT_TYPE NtProductType;
|
|
|
|
#define RPCDONE 0x1
|
|
#define LOCATORSTARTED 0x2
|
|
#define CRITSECSDONE 0x4
|
|
|
|
KdcState = Starting;
|
|
|
|
//
|
|
// Get the debugging parameters
|
|
//
|
|
|
|
GetDebugParams();
|
|
|
|
//
|
|
// Get other parameters, register wait on registry keys
|
|
//
|
|
|
|
KdcLoadParameters();
|
|
|
|
#ifdef ROGUE_DC
|
|
|
|
if ( ERROR_SUCCESS != RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE,
|
|
L"System\\CurrentControlSet\\Services\\Kdc\\Rogue",
|
|
0,
|
|
KEY_READ,
|
|
&hKdcRogueKey ))
|
|
{
|
|
DebugLog((DEB_WARN,"Failed to open \"rogue\" kerberos key\n" ));
|
|
}
|
|
|
|
#endif
|
|
|
|
hKdcParamEvent = CreateEvent(
|
|
NULL,
|
|
FALSE,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if (NULL == hKdcParamEvent)
|
|
{
|
|
D_DebugLog((DEB_WARN, "CreateEvent for KdcParamEvent failed - 0x%x\n", GetLastError()));
|
|
}
|
|
else
|
|
{
|
|
KerbWatchKdcParamKey( hKdcParamEvent, FALSE );
|
|
}
|
|
|
|
D_DebugLog((DEB_TRACE, "Start KdcServiceMain\n"));
|
|
|
|
//
|
|
// Notify the service controller that we are starting.
|
|
//
|
|
|
|
hService = RegisterServiceCtrlHandler(SERVICE_KDC, Handler);
|
|
if (!hService)
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Could not register handler, %d\n", GetLastError()));
|
|
}
|
|
|
|
SStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
|
|
SStatus.dwCurrentState = SERVICE_STOPPED;
|
|
SStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
|
SStatus.dwWin32ExitCode = 0;
|
|
SStatus.dwServiceSpecificExitCode = 0;
|
|
SStatus.dwCheckPoint = 0;
|
|
SStatus.dwWaitHint = 0;
|
|
|
|
if (!UpdateStatus(SERVICE_START_PENDING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Set up the event log service
|
|
//
|
|
|
|
InitializeEvents();
|
|
|
|
//
|
|
// Check out product type
|
|
//
|
|
|
|
if ( !RtlGetNtProductType( &NtProductType ) || ( NtProductType != NtProductLanManNt ) ) {
|
|
D_DebugLog((DEB_WARN, "Can't start KDC on non-lanmanNT systems\n"));
|
|
NtStatus = STATUS_MUST_BE_KDC;
|
|
goto Shutdown;
|
|
}
|
|
|
|
RtlInitUnicodeString(
|
|
&GlobalKerberosName,
|
|
MICROSOFT_KERBEROS_NAME_W
|
|
);
|
|
|
|
RtlInitUnicodeString(
|
|
&GlobalKdcName,
|
|
SERVICE_KDC
|
|
);
|
|
|
|
//
|
|
// Turn on safealloca support
|
|
//
|
|
SafeAllocaInitialize(SAFEALLOCA_USE_DEFAULT, SAFEALLOCA_USE_DEFAULT, KdcAllocate, KdcFree);
|
|
|
|
//
|
|
// Build our Kpasswd name, so we don't have to alloc on
|
|
// every TGS request.
|
|
//
|
|
NtStatus = KerbBuildKpasswdName(
|
|
&GlobalKpasswdName
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Failed to build KPASSWD name, error 0x%X\n", NtStatus));
|
|
goto Shutdown;
|
|
}
|
|
|
|
GlobalLocalhostAddress[0] = 127;
|
|
GlobalLocalhostAddress[1] = 0;
|
|
GlobalLocalhostAddress[2] = 0;
|
|
GlobalLocalhostAddress[3] = 1;
|
|
|
|
//
|
|
// Wait for SAM to start
|
|
//
|
|
|
|
if (!KdcWaitForSamService( ))
|
|
{
|
|
NtStatus = STATUS_INVALID_SERVER_STATE;
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Initialize the AuthZ resource manager
|
|
//
|
|
|
|
NtStatus = KdcInitializeAuthzRM();
|
|
if ( !NT_SUCCESS( NtStatus ))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Failed to intialize the AuthZ resource manager, error 0x%X\n", NtStatus));
|
|
goto Shutdown;
|
|
}
|
|
|
|
if (!UpdateStatus(SERVICE_START_PENDING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Can't proceed unless the kerb SSPI package has initialized
|
|
// (KerbKdcCallback might get invoked and that requires kerb
|
|
// global resource being intialized)
|
|
//
|
|
|
|
if (!KerbIsInitialized())
|
|
{
|
|
NtStatus = STATUS_UNSUCCESSFUL;
|
|
DebugLog((DEB_ERROR, "Kerb SSPI package not initialized: 0x%x\n",NtStatus));
|
|
goto Shutdown;
|
|
}
|
|
|
|
NtStatus = KerbInitPreferredCryptList();
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
DebugLog((DEB_ERROR, "KdcServiceMain could not initialized preferrred crypt list: 0x%x\n", NtStatus));
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Register for the Ds callback
|
|
//
|
|
|
|
if (!KdcRegisterToWaitForDS( ))
|
|
{
|
|
NtStatus = STATUS_INVALID_SERVER_STATE;
|
|
goto Shutdown;
|
|
}
|
|
|
|
if (!UpdateStatus(SERVICE_START_PENDING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Get a handle to the SAM account domain
|
|
//
|
|
|
|
NtStatus = OpenAccountDomain();
|
|
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
DebugLog((DEB_ERROR, "Failed to get domain handle: 0x%x\n",NtStatus));
|
|
goto Shutdown;
|
|
}
|
|
|
|
if (!UpdateStatus(SERVICE_START_PENDING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Initialize the PK infrastructure
|
|
//
|
|
|
|
NtStatus = KdcInitializeCerts();
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
D_DebugLog((DEB_ERROR,"Failed to initialize certs: 0x%x\n",NtStatus));
|
|
goto Shutdown;
|
|
}
|
|
|
|
if (!UpdateStatus(SERVICE_START_PENDING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Start the RPC sequences
|
|
//
|
|
|
|
NtStatus = StartAllProtSeqs();
|
|
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Failed to start RPC, error 0x%X\n", NtStatus));
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Start the socket listening code.
|
|
//
|
|
|
|
if (!UpdateStatus(SERVICE_START_PENDING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Load all global data into the SecData structure.
|
|
//
|
|
|
|
NtStatus = SecData.Init();
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Failed to init SecData error 0x%X\n", NtStatus));
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Set the flag to indicate this is a trust account
|
|
//
|
|
|
|
// KdcTicketInfo.UserAccountControl |= USER_INTERDOMAIN_TRUST_ACCOUNT;
|
|
|
|
if (!UpdateStatus(SERVICE_START_PENDING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Create the KDC shutdown event, set to FALSE
|
|
//
|
|
hKdcShutdownEvent = CreateEvent( NULL, // no security attributes
|
|
TRUE, // manual reset
|
|
FALSE, // initial state
|
|
NULL ); // unnamed event.
|
|
if (hKdcShutdownEvent == NULL)
|
|
{
|
|
NtStatus = (NTSTATUS) GetLastError();
|
|
|
|
D_DebugLog(( DEB_ERROR, "KDC can't create shutdown event: wincode=%d.\n",
|
|
NtStatus ));
|
|
|
|
goto Shutdown;
|
|
}
|
|
|
|
if (!UpdateStatus(SERVICE_START_PENDING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
#if DBG
|
|
NtStatus = RegisterKdcEps();
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Ep register failed %x\n", NtStatus));
|
|
// goto Shutdown;
|
|
}
|
|
#endif // DBG
|
|
|
|
ulStates |= RPCDONE;
|
|
|
|
//
|
|
// 1 is the minimum number of threads.
|
|
// TRUE means the call will return, rather than waiting until the
|
|
// server shuts down.
|
|
//
|
|
|
|
NtStatus = KdcInitializeSockets();
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Failed to initailzie sockets\n"));
|
|
goto Shutdown;
|
|
}
|
|
|
|
if (!UpdateStatus(SERVICE_START_PENDING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
InitializeListHead(&KdcDomainList);
|
|
NtStatus = (NTSTATUS) KdcReloadDomainTree( NULL );
|
|
if (!NT_SUCCESS(NtStatus))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Failed to build domain tree: 0x%x\n",NtStatus));
|
|
goto Shutdown;
|
|
}
|
|
|
|
if (!UpdateStatus(SERVICE_START_PENDING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
//
|
|
// Check to see if there is a CSP registered for replacing the StringToKey calculation
|
|
//
|
|
CheckForOutsideStringToKey();
|
|
|
|
if (!UpdateStatus(SERVICE_START_PENDING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
NtStatus = LsaIKerberosRegisterTrustNotification( KdcTrustChangeCallback, LsaRegister );
|
|
if (!NT_SUCCESS( NtStatus ))
|
|
{
|
|
D_DebugLog((DEB_ERROR, "Failed to register notification\n"));
|
|
}
|
|
|
|
|
|
RpcStatus = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, TRUE);
|
|
|
|
if (RpcStatus != ERROR_SUCCESS)
|
|
{
|
|
if (RpcStatus != RPC_S_ALREADY_LISTENING)
|
|
{
|
|
D_DebugLog(( DEB_ERROR, "Error from RpcServerListen: %d\n", RpcStatus ));
|
|
NtStatus = I_RpcMapWin32Status(RpcStatus);
|
|
goto Shutdown;
|
|
}
|
|
}
|
|
|
|
// Initialize the GlobalCert
|
|
KdcMyStoreWaitHandler (NULL, TRUE);
|
|
|
|
//
|
|
// At this point the KDC is officially started.
|
|
// 3 * ( 2*(hip) horray! )
|
|
//
|
|
|
|
if (!UpdateStatus(SERVICE_RUNNING) )
|
|
{
|
|
goto Shutdown;
|
|
}
|
|
|
|
#if DBG
|
|
GetSystemTimeAsFileTime((PFILETIME)&tsOut);
|
|
D_DebugLog((DEB_TRACE, "Time required for KDC to start up: %d ms\n",
|
|
(tsOut.LowPart-tsIn.LowPart) / 10000));
|
|
#endif
|
|
|
|
|
|
SetKdcStartEvent();
|
|
|
|
KdcState = Running;
|
|
|
|
KdcInitializeTrace();
|
|
|
|
// This function will loop until the event is true.
|
|
|
|
// WAS BUG: turn off cache manager for now.
|
|
// This bug comment is a stale piece of code from
|
|
// Cairo days - per MikeSw
|
|
//
|
|
|
|
WaitForSingleObject(hKdcShutdownEvent, INFINITE);
|
|
|
|
Shutdown:
|
|
|
|
LsaIKerberosRegisterTrustNotification( KdcTrustChangeCallback, LsaUnregister );
|
|
LsaIUnregisterAllPolicyChangeNotificationCallback(KdcPolicyChangeCallBack);
|
|
|
|
//
|
|
// Time to cleanup up all resources ...
|
|
//
|
|
|
|
TempStatus = I_NetLogonSetServiceBits( DS_KDC_FLAG, 0 );
|
|
if ( !NT_SUCCESS(TempStatus) ) {
|
|
D_DebugLog((DEB_TRACE,"Can't tell netlogon we're stopped 0x%lX\n", TempStatus ));
|
|
}
|
|
|
|
//
|
|
// Remove the wait routine for the DS paused event
|
|
//
|
|
|
|
if ( KdcGlobalDsPausedWaitHandle != NULL ) {
|
|
|
|
UnregisterWaitEx( KdcGlobalDsPausedWaitHandle,
|
|
INVALID_HANDLE_VALUE ); // Wait until routine finishes execution
|
|
|
|
KdcGlobalDsPausedWaitHandle = NULL;
|
|
}
|
|
|
|
if (NULL != GlobalKpasswdName)
|
|
{
|
|
KerbFreeKdcName(&GlobalKpasswdName);
|
|
}
|
|
|
|
if (KdcGlobalDsEventHandle)
|
|
{
|
|
CloseHandle( KdcGlobalDsEventHandle );
|
|
KdcGlobalDsEventHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// Shut down event log service.
|
|
//
|
|
ShutdownEvents();
|
|
|
|
|
|
UpdateStatus(SERVICE_STOP_PENDING);
|
|
|
|
|
|
#if DBG
|
|
if (ulStates & RPCDONE)
|
|
{
|
|
(VOID)UnRegisterKdcEps();
|
|
UpdateStatus(SERVICE_STOP_PENDING);
|
|
}
|
|
#endif // DBG
|
|
|
|
KdcShutdownSockets();
|
|
|
|
KdcFreeAuthzRm();
|
|
|
|
KdcCleanupCerts(
|
|
TRUE // cleanup scavenger
|
|
);
|
|
|
|
UpdateStatus(SERVICE_STOP_PENDING);
|
|
|
|
//
|
|
// Close all of the events.
|
|
//
|
|
|
|
{
|
|
PHANDLE ph = &hKdcHandles[0];
|
|
|
|
for (;ph < &hKdcHandles[MAX_KDC_HANDLE]; ph++)
|
|
{
|
|
if (*ph)
|
|
{
|
|
CloseHandle(*ph);
|
|
*ph = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( hKdcParamEvent ) {
|
|
WaitKdcCleanup( hKdcParamEvent );
|
|
}
|
|
|
|
//
|
|
// Cleanup handles to SAM & LSA and global variables
|
|
//
|
|
|
|
CleanupAccountDomain();
|
|
|
|
UpdateStatus(SERVICE_STOP_PENDING);
|
|
|
|
//
|
|
// Cleanup the domain list
|
|
//
|
|
|
|
//
|
|
// BUGBUG: need to make sure it is not being used.
|
|
//
|
|
|
|
KdcFreeDomainList(&KdcDomainList);
|
|
KdcFreeReferralCache(&KdcReferralCache);
|
|
|
|
if (kdc_pPreferredCryptList)
|
|
{
|
|
KerbFreeCryptList(kdc_pPreferredCryptList);
|
|
}
|
|
if (kdc_pMitPrincipalPreferredCryptList)
|
|
{
|
|
KerbFreeCryptList(kdc_pMitPrincipalPreferredCryptList);
|
|
}
|
|
|
|
#ifdef ROGUE_DC
|
|
if ( hKdcRogueKey )
|
|
{
|
|
RegCloseKey( hKdcRogueKey );
|
|
}
|
|
#endif
|
|
|
|
SStatus.dwWin32ExitCode = RtlNtStatusToDosError(NtStatus);
|
|
SStatus.dwServiceSpecificExitCode = 0;
|
|
|
|
D_DebugLog(( DEB_TRACE, "KDC shutting down.\n" ));
|
|
UpdateStatus(SERVICE_STOPPED);
|
|
D_DebugLog((DEB_TRACE, "End KdcServiceMain\n"));
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: ShutDown
|
|
//
|
|
// Synopsis: Shuts the KDC down.
|
|
//
|
|
// Arguments: pszMessage - message to print to debug port
|
|
//
|
|
// Notes: Stops RPC from accepting new calls, waits for pending calls
|
|
// to finish, and sets the global event "hKdcShutDownEvent".
|
|
//
|
|
NTSTATUS
|
|
ShutDown(LPWSTR pszMessage)
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
TRACE(KDC, ShutDown, DEB_FUNCTION);
|
|
|
|
D_DebugLog((DEB_WARN, "Server Shutdown: %ws\n", pszMessage));
|
|
|
|
|
|
//
|
|
// Notify the all threads that we are exiting.
|
|
//
|
|
|
|
|
|
//
|
|
// First set the started flag to false so nobody will try any more
|
|
// direct calls to the KDC.
|
|
//
|
|
|
|
KdcState = Stopped;
|
|
|
|
//
|
|
// If there are any outstanding calls, let them trigger the shutdown event.
|
|
// Otherwise set the shutdown event ourselves.
|
|
//
|
|
|
|
EnterCriticalSection(&ApiCriticalSection);
|
|
if (CurrentApiCallers == 0)
|
|
{
|
|
|
|
if (!SetEvent( hKdcShutdownEvent ) )
|
|
{
|
|
D_DebugLog(( DEB_ERROR, "Couldn't set KDC shutdown event. winerr=%d.\n",
|
|
GetLastError() ));
|
|
NtStatus = STATUS_UNSUCCESSFUL;
|
|
}
|
|
SecData.Cleanup();
|
|
if (KdcTraceRegistrationHandle != (TRACEHANDLE)0)
|
|
{
|
|
UnregisterTraceGuids( KdcTraceRegistrationHandle );
|
|
KdcTraceRegistrationHandle = (TRACEHANDLE)0;
|
|
}
|
|
}
|
|
LeaveCriticalSection(&ApiCriticalSection);
|
|
|
|
return(NtStatus);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: DllMain
|
|
//
|
|
// Synopsis: DLL initialization routine
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
extern "C" BOOL WINAPI
|
|
DllMain (
|
|
HINSTANCE hInstance,
|
|
DWORD dwReason,
|
|
PVOID lpReserved
|
|
)
|
|
{
|
|
BOOL bReturn = TRUE;
|
|
|
|
if ( dwReason == DLL_PROCESS_ATTACH )
|
|
{
|
|
DisableThreadLibraryCalls ( hInstance );
|
|
|
|
//
|
|
// WAS BUG: call the Rtl version here because it returns an error
|
|
// instead of throwing an exception. Leave it here, as we don't
|
|
// really need to put a try/except around InitCritSec.
|
|
//
|
|
|
|
bReturn = NT_SUCCESS(RtlInitializeCriticalSection( &ApiCriticalSection ));
|
|
|
|
if (bReturn)
|
|
{
|
|
bReturn = NT_SUCCESS(SecData.InitLock());
|
|
|
|
if (!bReturn)
|
|
{
|
|
RtlDeleteCriticalSection(&ApiCriticalSection);
|
|
}
|
|
}
|
|
}
|
|
else if (dwReason == DLL_PROCESS_DETACH)
|
|
{
|
|
DeleteCriticalSection(&ApiCriticalSection);
|
|
}
|
|
|
|
return bReturn;
|
|
UNREFERENCED_PARAMETER(lpReserved);
|
|
UNREFERENCED_PARAMETER(hInstance);
|
|
|
|
}
|
|
|
|
|
|
|