1195 lines
30 KiB
C++
1195 lines
30 KiB
C++
|
//+-----------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation 1992 - 1996
|
||
|
//
|
||
|
// File: rpcutil.cxx
|
||
|
//
|
||
|
// Contents: Utilities for RPC for Kerberos package
|
||
|
//
|
||
|
//
|
||
|
// History: 19-April-1996 Created MikeSw
|
||
|
//
|
||
|
//------------------------------------------------------------------------
|
||
|
|
||
|
#include <kerb.hxx>
|
||
|
|
||
|
#include <kerbp.h>
|
||
|
|
||
|
#ifdef RETAIL_LOG_SUPPORT
|
||
|
static TCHAR THIS_FILE[]=TEXT(__FILE__);
|
||
|
#endif
|
||
|
|
||
|
extern "C"
|
||
|
{
|
||
|
#include <lmaccess.h>
|
||
|
#include <lmapibuf.h>
|
||
|
#include <lmerr.h>
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
#include <netlibnt.h>
|
||
|
#endif // WIN32_CHICAGO
|
||
|
}
|
||
|
|
||
|
|
||
|
SOCKET KerbPNPSocket;
|
||
|
HANDLE KerbPNPSocketEvent = NULL;
|
||
|
HANDLE KerbPNPSocketWaitHandle = NULL;
|
||
|
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
// The problem here is that we need to read the domain info once after
|
||
|
// joining domain. (for rebootless join) and once after the reboot
|
||
|
// (if there was one). We can only delete the data after a reboot.
|
||
|
|
||
|
// This one controls whether I read the domain info that JoinDomain dumps
|
||
|
// in the registry.
|
||
|
BOOLEAN fNewDataAboutDomain = TRUE;
|
||
|
|
||
|
// This one controls when I should delete the domain info that JoinDomain
|
||
|
// dumps in the registry
|
||
|
BOOLEAN fRebootedSinceJoin = TRUE;
|
||
|
|
||
|
RTL_CRITICAL_SECTION KerbCallKdcDataLock;
|
||
|
BOOLEAN KerbCallKdcDataInitialized = FALSE;
|
||
|
|
||
|
#define KerbLockKdcData() (RtlEnterCriticalSection(&KerbCallKdcDataLock))
|
||
|
#define KerbUnlockKdcData() (RtlLeaveCriticalSection(&KerbCallKdcDataLock))
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
NTSTATUS
|
||
|
KerbInitKdcData()
|
||
|
{
|
||
|
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
if (!KerbCallKdcDataInitialized)
|
||
|
{
|
||
|
Status = RtlInitializeCriticalSection(&KerbCallKdcDataLock);
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
KerbCallKdcDataInitialized = TRUE;
|
||
|
}
|
||
|
Cleanup:
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
KerbFreeKdcData()
|
||
|
{
|
||
|
if (KerbCallKdcDataInitialized)
|
||
|
{
|
||
|
RtlDeleteCriticalSection(&KerbCallKdcDataLock);
|
||
|
KerbCallKdcDataInitialized = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
KerbSetKdcData(BOOLEAN fNewDomain, BOOLEAN fRebooted)
|
||
|
{
|
||
|
KerbLockKdcData();
|
||
|
|
||
|
fNewDataAboutDomain = fNewDomain;
|
||
|
fRebootedSinceJoin = fRebooted;
|
||
|
|
||
|
KerbUnlockKdcData();
|
||
|
}
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: MIDL_user_allocate
|
||
|
//
|
||
|
// Synopsis: Allocation routine for use by RPC client stubs
|
||
|
//
|
||
|
// Effects: allocates memory with LsaFunctions.AllocateLsaHeap
|
||
|
//
|
||
|
// Arguments: BufferSize - size of buffer, in bytes, to allocate
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: Buffer pointer or NULL on allocation failure
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
PVOID
|
||
|
MIDL_user_allocate(
|
||
|
IN size_t BufferSize
|
||
|
)
|
||
|
{
|
||
|
return(KerbAllocate( ROUND_UP_COUNT(BufferSize, 8) ) );
|
||
|
}
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: MIDL_user_free
|
||
|
//
|
||
|
// Synopsis: Memory free routine for RPC client stubs
|
||
|
//
|
||
|
// Effects: frees the buffer with LsaFunctions.FreeLsaHeap
|
||
|
//
|
||
|
// Arguments: Buffer - Buffer to free
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: none
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
|
||
|
VOID
|
||
|
MIDL_user_free(
|
||
|
IN PVOID Buffer
|
||
|
)
|
||
|
{
|
||
|
KerbFree( Buffer );
|
||
|
}
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
#define NETSETUPP_NETLOGON_PARAMETERS \
|
||
|
L"SYSTEM\\CurrentControlSet\\Services\\Netlogon\\Parameters"
|
||
|
#define NETSETUPP_NETLOGON_KERB_JD\
|
||
|
L"KerbIsDoneWithJoinDomainEntry"
|
||
|
#define NETSETUPP_NETLOGON_JD \
|
||
|
L"SYSTEM\\CurrentControlSet\\Services\\Netlogon\\JoinDomain"
|
||
|
#define NETSETUPP_NETLOGON_JD_DCA L"DomainControllerAddress"
|
||
|
#define NETSETUPP_NETLOGON_JD_DCAT L"DomainControllerAddressType"
|
||
|
#define NETSETUPP_NETLOGON_JD_F L"Flags"
|
||
|
|
||
|
BOOLEAN
|
||
|
ReadInitialDcRecord(PUNICODE_STRING uString,
|
||
|
PULONG RegAddressType,
|
||
|
PULONG RegFlags)
|
||
|
{
|
||
|
ULONG WinError = ERROR_SUCCESS;
|
||
|
HKEY hJoinKey = NULL, hParametersKey = NULL;
|
||
|
ULONG Flags = 0, AddressType = 0, KdcNameSize = 0, dwTRUE = 1, Type =0;
|
||
|
LPWSTR KdcName = NULL;
|
||
|
BOOLEAN fReadCache = FALSE;
|
||
|
ULONG dwsize = sizeof(ULONG);
|
||
|
USHORT TempLen = 0;
|
||
|
|
||
|
RtlInitUnicodeString(
|
||
|
uString,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
*RegAddressType = 0;
|
||
|
*RegFlags = 0;
|
||
|
|
||
|
WinError = RegOpenKey( HKEY_LOCAL_MACHINE,
|
||
|
NETSETUPP_NETLOGON_JD,
|
||
|
&hJoinKey);
|
||
|
|
||
|
if ( WinError != ERROR_SUCCESS) {
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
WinError = RegQueryValueEx( hJoinKey,
|
||
|
NETSETUPP_NETLOGON_JD_DCA,
|
||
|
0,
|
||
|
&Type,
|
||
|
NULL,
|
||
|
&KdcNameSize);
|
||
|
|
||
|
if ( WinError != ERROR_SUCCESS) {
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
KdcName = (LPWSTR) KerbAllocate(KdcNameSize);
|
||
|
|
||
|
if (KdcName == NULL)
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
WinError = RegQueryValueEx( hJoinKey,
|
||
|
NETSETUPP_NETLOGON_JD_DCA,
|
||
|
0,
|
||
|
&Type,
|
||
|
(PUCHAR) KdcName,
|
||
|
&KdcNameSize);
|
||
|
|
||
|
if ( WinError != ERROR_SUCCESS) {
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
WinError = RegQueryValueEx( hJoinKey,
|
||
|
NETSETUPP_NETLOGON_JD_DCAT,
|
||
|
0,
|
||
|
&Type,
|
||
|
(PUCHAR)&AddressType,
|
||
|
&dwsize );
|
||
|
|
||
|
if ( WinError != ERROR_SUCCESS) {
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
WinError = RegQueryValueEx( hJoinKey,
|
||
|
NETSETUPP_NETLOGON_JD_F,
|
||
|
0,
|
||
|
&Type,
|
||
|
(PUCHAR)&Flags,
|
||
|
&dwsize);
|
||
|
|
||
|
if ( WinError != ERROR_SUCCESS) {
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
TempLen = (USHORT)wcslen(KdcName+2);
|
||
|
|
||
|
uString->Buffer = (PWSTR) KerbAllocate ((TempLen + 1) *sizeof(WCHAR));
|
||
|
|
||
|
if (uString->Buffer == NULL)
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
wcscpy(uString->Buffer,
|
||
|
KdcName+2);
|
||
|
|
||
|
uString->Length = TempLen * sizeof(WCHAR);
|
||
|
uString->MaximumLength = uString->Length + sizeof(WCHAR);
|
||
|
uString->Buffer[TempLen] = L'\0';
|
||
|
|
||
|
*RegAddressType = AddressType;
|
||
|
*RegFlags = Flags;
|
||
|
|
||
|
// Now set the reg entry so that netlogon knows that we've read it.
|
||
|
|
||
|
if (fRebootedSinceJoin)
|
||
|
{
|
||
|
WinError = RegOpenKey( HKEY_LOCAL_MACHINE,
|
||
|
NETSETUPP_NETLOGON_PARAMETERS,
|
||
|
&hParametersKey);
|
||
|
|
||
|
if ( WinError != ERROR_SUCCESS) {
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
WinError = RegSetValueEx( hParametersKey,
|
||
|
NETSETUPP_NETLOGON_KERB_JD,
|
||
|
0,
|
||
|
REG_DWORD,
|
||
|
(PUCHAR)&dwTRUE,
|
||
|
sizeof(DWORD));
|
||
|
|
||
|
if ( WinError != ERROR_SUCCESS) {
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
DebugLog((DEB_ERROR, "Setting DoneWJoin key!\n"));
|
||
|
}
|
||
|
|
||
|
fReadCache = TRUE;
|
||
|
|
||
|
|
||
|
Cleanup:
|
||
|
|
||
|
KerbSetKdcData(FALSE, fRebootedSinceJoin);
|
||
|
|
||
|
if (hJoinKey)
|
||
|
{
|
||
|
RegCloseKey( hJoinKey );
|
||
|
}
|
||
|
|
||
|
if (hParametersKey)
|
||
|
{
|
||
|
RegCloseKey( hParametersKey );
|
||
|
}
|
||
|
|
||
|
if (KdcName)
|
||
|
{
|
||
|
KerbFree(KdcName);
|
||
|
}
|
||
|
return (fReadCache);
|
||
|
}
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbSocketChangeHandler
|
||
|
//
|
||
|
// Synopsis: Simply setups up a WSA Pnp event, so that we catch network
|
||
|
// changes.
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
VOID
|
||
|
KerbSocketChangeHandler(
|
||
|
VOID* Context,
|
||
|
BOOLEAN WaitStatus
|
||
|
)
|
||
|
{
|
||
|
|
||
|
WSAPROTOCOL_INFO *lpProtocolBuf = NULL;
|
||
|
DWORD dwBufLen = 0;
|
||
|
INT protocols[2];
|
||
|
int nRet = 0;
|
||
|
NTSTATUS Status;
|
||
|
NET_API_STATUS NetStatus;
|
||
|
|
||
|
//
|
||
|
// Determine if TCP is installed.
|
||
|
//
|
||
|
protocols[0] = IPPROTO_TCP;
|
||
|
protocols[1] = NULL;
|
||
|
|
||
|
nRet = WSAEnumProtocols(protocols, lpProtocolBuf, &dwBufLen);
|
||
|
|
||
|
KerbGlobalWriteLock();
|
||
|
if (nRet == 0)
|
||
|
{
|
||
|
D_DebugLog((DEB_TRACE, "Turning OFF! TCP %x\n", nRet));
|
||
|
KerbGlobalNoTcpUdp = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
KerbGlobalNoTcpUdp = FALSE;
|
||
|
if (nRet == SOCKET_ERROR)
|
||
|
{
|
||
|
nRet = WSAGetLastError();
|
||
|
}
|
||
|
|
||
|
D_DebugLog((DEB_TRACE, "Turning on TCP %x\n", nRet));
|
||
|
}
|
||
|
KerbGlobalReleaseLock();
|
||
|
|
||
|
//
|
||
|
// Re-Register the wait - but make sure the values are init'd / correct.
|
||
|
//
|
||
|
KerbLockKdcData();
|
||
|
|
||
|
if ( KerbPNPSocketWaitHandle && KerbPNPSocketEvent )
|
||
|
{
|
||
|
NetStatus = WSAEventSelect(
|
||
|
KerbPNPSocket,
|
||
|
KerbPNPSocketEvent,
|
||
|
FD_ADDRESS_LIST_CHANGE
|
||
|
);
|
||
|
|
||
|
if ( NetStatus != 0 )
|
||
|
{
|
||
|
NetStatus = WSAGetLastError();
|
||
|
DebugLog(( DEB_ERROR, "Can't WSAEventSelect %ld\n", NetStatus ));
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
Status = RtlRegisterWait(
|
||
|
&KerbPNPSocketWaitHandle,
|
||
|
KerbPNPSocketEvent,
|
||
|
KerbSocketChangeHandler,
|
||
|
NULL,
|
||
|
INFINITE,
|
||
|
WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS( Status ))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR, "RtlRegisterWait failed %x\n", Status));
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Issue the IOCTL, so we'll get notified.
|
||
|
//
|
||
|
NetStatus = WSAIoctl(
|
||
|
KerbPNPSocket,
|
||
|
SIO_ADDRESS_LIST_CHANGE,
|
||
|
NULL, // No input buffer
|
||
|
0, // No input buffer
|
||
|
NULL, // No output buffer
|
||
|
0, // No output buffer
|
||
|
&dwBufLen,
|
||
|
NULL, // No overlapped,
|
||
|
NULL // Not async
|
||
|
);
|
||
|
|
||
|
if ( NetStatus != 0 )
|
||
|
{
|
||
|
NetStatus = WSAGetLastError();
|
||
|
if ( NetStatus != WSAEWOULDBLOCK)
|
||
|
{
|
||
|
DebugLog((DEB_ERROR, "WSAIOCTL failed %x\n", NetStatus));
|
||
|
Status = STATUS_INTERNAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
KerbUnlockKdcData();
|
||
|
KerbCleanupBindingCache(FALSE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbInitNetworkChangeEvent
|
||
|
//
|
||
|
// Synopsis: Simply setups up a WSA Pnp event, so that we catch network
|
||
|
// changes.
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments:
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns:
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
NTSTATUS
|
||
|
KerbInitNetworkChangeEvent()
|
||
|
{
|
||
|
|
||
|
NET_API_STATUS NetStatus;
|
||
|
NTSTATUS Status = STATUS_INTERNAL_ERROR;
|
||
|
DWORD BytesReturned;
|
||
|
|
||
|
KerbLockKdcData();
|
||
|
|
||
|
if ( KerbPNPSocket != NULL )
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Open a socket to get winsock PNP notifications on.
|
||
|
//
|
||
|
KerbPNPSocket = WSASocket(
|
||
|
AF_INET,
|
||
|
SOCK_DGRAM,
|
||
|
0, // PF_INET,
|
||
|
NULL,
|
||
|
0,
|
||
|
0
|
||
|
);
|
||
|
|
||
|
if ( KerbPNPSocket == INVALID_SOCKET )
|
||
|
{
|
||
|
NetStatus = WSAGetLastError();
|
||
|
|
||
|
DebugLog(( DEB_ERROR, "Can't WSASocket %ld\n", NetStatus ));
|
||
|
|
||
|
if ( NetStatus == WSAEAFNOSUPPORT )
|
||
|
{
|
||
|
Status = STATUS_SUCCESS;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Open an event to wait on.
|
||
|
//
|
||
|
KerbPNPSocketEvent = CreateEvent(
|
||
|
NULL, // No security ettibutes
|
||
|
FALSE, // Auto reset
|
||
|
FALSE, // Initially not signaled
|
||
|
NULL
|
||
|
);
|
||
|
if ( KerbPNPSocketEvent == NULL )
|
||
|
{
|
||
|
DebugLog(( DEB_ERROR,"Cannot create Winsock PNP event %ld\n", GetLastError() ));
|
||
|
Status = STATUS_INTERNAL_ERROR;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
NetStatus = WSAEventSelect(
|
||
|
KerbPNPSocket,
|
||
|
KerbPNPSocketEvent,
|
||
|
FD_ADDRESS_LIST_CHANGE
|
||
|
);
|
||
|
|
||
|
if ( NetStatus != 0 )
|
||
|
{
|
||
|
NetStatus = WSAGetLastError();
|
||
|
DebugLog(( DEB_ERROR, "Can't WSAEventSelect %ld\n", NetStatus ));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Register a wait.
|
||
|
//
|
||
|
Status = RtlRegisterWait(
|
||
|
&KerbPNPSocketWaitHandle,
|
||
|
KerbPNPSocketEvent,
|
||
|
KerbSocketChangeHandler,
|
||
|
NULL,
|
||
|
INFINITE,
|
||
|
WT_EXECUTEDEFAULT | WT_EXECUTEONLYONCE
|
||
|
);
|
||
|
|
||
|
|
||
|
if (!NT_SUCCESS( Status ))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR, "RtlRegisterWait failed %x\n", Status));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Issue the IOCTL, so we'll get notified.
|
||
|
//
|
||
|
NetStatus = WSAIoctl(
|
||
|
KerbPNPSocket,
|
||
|
SIO_ADDRESS_LIST_CHANGE,
|
||
|
NULL, // No input buffer
|
||
|
0, // No input buffer
|
||
|
NULL, // No output buffer
|
||
|
0, // No output buffer
|
||
|
&BytesReturned,
|
||
|
NULL, // No overlapped,
|
||
|
NULL // Not async
|
||
|
);
|
||
|
|
||
|
if ( NetStatus != 0 )
|
||
|
{
|
||
|
NetStatus = WSAGetLastError();
|
||
|
if ( NetStatus != WSAEWOULDBLOCK) {
|
||
|
DebugLog((DEB_ERROR, "WSAIOCTL failed %x\n", NetStatus));
|
||
|
Status = STATUS_INTERNAL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
Cleanup:
|
||
|
|
||
|
if (!NT_SUCCESS( Status ))
|
||
|
{
|
||
|
if ( KerbPNPSocketWaitHandle )
|
||
|
{
|
||
|
RtlDeregisterWait(KerbPNPSocketWaitHandle);
|
||
|
KerbPNPSocketWaitHandle = NULL;
|
||
|
}
|
||
|
|
||
|
if ( KerbPNPSocketEvent )
|
||
|
{
|
||
|
CloseHandle( KerbPNPSocketEvent );
|
||
|
KerbPNPSocketEvent = NULL;
|
||
|
}
|
||
|
|
||
|
if ( KerbPNPSocket )
|
||
|
{
|
||
|
closesocket( KerbPNPSocket );
|
||
|
KerbPNPSocket = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KerbUnlockKdcData();
|
||
|
|
||
|
return Status;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// This address type is used for talking to MIT KDCs
|
||
|
//
|
||
|
|
||
|
#define DS_UNKNOWN_ADDRESS_TYPE 0
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Function: KerbGetKdcBinding
|
||
|
//
|
||
|
// Synopsis: Gets a binding to the KDC in the specified domain
|
||
|
//
|
||
|
// Effects:
|
||
|
//
|
||
|
// Arguments: Realm - Domain in which to find KDC
|
||
|
// PrincipalName - name of a principal that must be on the KDC
|
||
|
// DesiredFlags - Flags to pass to the locator.
|
||
|
// FindKpasswd - if domain is an MIT realm, then returns Kpasswd
|
||
|
// addresses instead of KDC addresses KDC
|
||
|
// BindingCacheEntry - receives binding handle cache entry for
|
||
|
// TCP to the KDC
|
||
|
//
|
||
|
// Requires:
|
||
|
//
|
||
|
// Returns: RPC errors, NET API errors
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
NTSTATUS
|
||
|
KerbGetKdcBinding(
|
||
|
IN PUNICODE_STRING RealmName,
|
||
|
IN OPTIONAL PUNICODE_STRING PrincipalName,
|
||
|
IN ULONG DesiredFlags,
|
||
|
IN BOOLEAN FindKpasswd,
|
||
|
IN BOOLEAN UseTcp,
|
||
|
OUT PKERB_BINDING_CACHE_ENTRY * BindingCacheEntry
|
||
|
)
|
||
|
{
|
||
|
NTSTATUS Status = STATUS_NETLOGON_NOT_STARTED;
|
||
|
NTSTATUS TempStatus = STATUS_SUCCESS;
|
||
|
LPWSTR DomainName = NULL;
|
||
|
LPWSTR AccountName = NULL;
|
||
|
ULONG NetStatus = 0;
|
||
|
ULONG AddressType = DS_INET_ADDRESS;
|
||
|
UNICODE_STRING KdcNameString = {0};
|
||
|
PDOMAIN_CONTROLLER_INFOW DcInfo = NULL;
|
||
|
PKERB_MIT_REALM MitRealm = NULL;
|
||
|
BOOLEAN UsedAlternateName;
|
||
|
BOOLEAN CacheDc = FALSE;
|
||
|
BOOLEAN CalledNetlogonDirectly = FALSE;
|
||
|
KERBEROS_MACHINE_ROLE Role;
|
||
|
ULONG ActualFlags = 0;
|
||
|
ULONG CacheFlags = 0, DcFlags = 0;
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
ULONG CachedAddressType = DS_INET_ADDRESS;
|
||
|
ULONG CachedFlags = 0;
|
||
|
UNICODE_STRING CachedKdcNameString = {0};
|
||
|
UNICODE_STRING LocalMachineName;
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
Role = KerbGetGlobalRole();
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
|
||
|
LocalMachineName.Buffer = NULL;
|
||
|
|
||
|
KerbGlobalReadLock();
|
||
|
|
||
|
//
|
||
|
// use TempStatus to retain initial Status value.
|
||
|
//
|
||
|
|
||
|
TempStatus = KerbDuplicateString( &LocalMachineName, &KerbGlobalMachineName );
|
||
|
KerbGlobalReleaseLock();
|
||
|
|
||
|
|
||
|
if(!NT_SUCCESS(TempStatus))
|
||
|
{
|
||
|
DebugLog((DEB_ERROR, "Failed to duplicate KerbGlobalMachineName\n"));
|
||
|
Status = TempStatus;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
if ((Role != KerbRoleDomainController) &&
|
||
|
(RtlEqualDomainName(
|
||
|
RealmName,
|
||
|
&LocalMachineName
|
||
|
)))
|
||
|
{
|
||
|
//
|
||
|
// This is an attempt at a local logon on a workstation. We don't do that.
|
||
|
//
|
||
|
|
||
|
DebugLog((DEB_WARN, "Attempting to locate a KDC for the workstation - failing\n"));
|
||
|
Status = STATUS_NO_LOGON_SERVERS;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we are logging on for the first time after join domain, the
|
||
|
// following registry entry should exist.
|
||
|
// Cache that entry, we should try to get to that dc.
|
||
|
// Don't cache the entry if we are a dc
|
||
|
// Don't cache the entry if this is not for our domain
|
||
|
//
|
||
|
|
||
|
if ( fNewDataAboutDomain &&
|
||
|
KerbGlobalRole != KerbRoleDomainController)
|
||
|
{
|
||
|
if (ReadInitialDcRecord(&CachedKdcNameString, &CachedAddressType, &CachedFlags))
|
||
|
{
|
||
|
KerbFreeString(&KerbGlobalInitialDcRecord);
|
||
|
RtlInitUnicodeString(
|
||
|
&KerbGlobalInitialDcRecord,
|
||
|
CachedKdcNameString.Buffer
|
||
|
);
|
||
|
|
||
|
KerbGlobalInitialDcAddressType = CachedAddressType;
|
||
|
KerbGlobalInitialDcFlags = CachedFlags;
|
||
|
|
||
|
CacheDc = TRUE;
|
||
|
}
|
||
|
else if (KerbGlobalInitialDcRecord.Buffer != NULL)
|
||
|
{
|
||
|
CacheDc = TRUE;
|
||
|
}
|
||
|
|
||
|
if (CacheDc && KerbIsThisOurDomain( RealmName))
|
||
|
{
|
||
|
PKERB_BINDING_CACHE_ENTRY TempBindingCacheEntry = NULL;
|
||
|
|
||
|
TempStatus = KerbCacheBinding(
|
||
|
RealmName,
|
||
|
&KerbGlobalInitialDcRecord,
|
||
|
KerbGlobalInitialDcAddressType,
|
||
|
DesiredFlags,
|
||
|
KerbGlobalInitialDcFlags,
|
||
|
0,
|
||
|
&TempBindingCacheEntry
|
||
|
);
|
||
|
|
||
|
if ( TempBindingCacheEntry ) {
|
||
|
|
||
|
KerbDereferenceBindingCacheEntry( TempBindingCacheEntry );
|
||
|
}
|
||
|
|
||
|
KerbFreeString(&KerbGlobalInitialDcRecord);
|
||
|
}
|
||
|
}
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
//
|
||
|
// Check the cache if we don't want to force rediscovery
|
||
|
//
|
||
|
|
||
|
if ((DesiredFlags & DS_FORCE_REDISCOVERY) == 0)
|
||
|
{
|
||
|
*BindingCacheEntry = KerbLocateBindingCacheEntry(
|
||
|
RealmName,
|
||
|
DesiredFlags,
|
||
|
FALSE
|
||
|
);
|
||
|
|
||
|
|
||
|
if (NULL != *BindingCacheEntry)
|
||
|
{
|
||
|
//
|
||
|
// For realmless workstations, we add negative cache entries.
|
||
|
// This is because the netlogon service doesn't run on non-joined
|
||
|
// machines, so we don't have the benefit of its negative caching.
|
||
|
// The end result is that we need to do that ourselves.
|
||
|
//
|
||
|
if (( Role == KerbRoleRealmlessWksta ) &&
|
||
|
(( (*BindingCacheEntry)->CacheFlags & KERB_BINDING_NEGATIVE_ENTRY ) != 0))
|
||
|
{
|
||
|
DebugLog((DEB_TRACE, "Found negative entry for %wZ\n", RealmName));
|
||
|
Status = STATUS_NO_LOGON_SERVERS;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Status = STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// If we are a domain controller, then we can simply use our global
|
||
|
// computer name.
|
||
|
//
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
if ((Role == KerbRoleDomainController) &&
|
||
|
((DesiredFlags & DS_PDC_REQUIRED) == 0) &&
|
||
|
KerbIsThisOurDomain(
|
||
|
RealmName
|
||
|
))
|
||
|
{
|
||
|
DsysAssert(LocalMachineName.Buffer[LocalMachineName.Length/sizeof(WCHAR)] == L'\0');
|
||
|
|
||
|
|
||
|
if (!KerbKdcStarted)
|
||
|
{
|
||
|
Status = KerbWaitForKdc( KerbGlobalKdcWaitTime ); // wait for KerbGlobalKdcWaitTime seconds
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
KerbKdcStarted = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugLog((DEB_WARN, "Failed to wait for KDC to start: 0x%x\n",Status));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (KerbKdcStarted)
|
||
|
{
|
||
|
Status = STATUS_SUCCESS;
|
||
|
KdcNameString = LocalMachineName;
|
||
|
AddressType = DS_NETBIOS_ADDRESS;
|
||
|
CacheFlags |= KERB_BINDING_LOCAL;
|
||
|
DcFlags |= DS_CLOSEST_FLAG;
|
||
|
}
|
||
|
}
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
//
|
||
|
// Check to see if this is an MIT realm
|
||
|
//
|
||
|
|
||
|
if (!NT_SUCCESS(Status) &&
|
||
|
KerbLookupMitRealmWithSrvLookup(
|
||
|
RealmName,
|
||
|
&MitRealm,
|
||
|
FindKpasswd,
|
||
|
UseTcp
|
||
|
))
|
||
|
{
|
||
|
LONG ServerIndex;
|
||
|
PKERB_MIT_SERVER_LIST ServerList;
|
||
|
|
||
|
if ((MitRealm->Flags & KERB_MIT_REALM_TCP_SUPPORTED) == 0)
|
||
|
{
|
||
|
CacheFlags |= KERB_BINDING_NO_TCP;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Pick which of the servers we want to use.
|
||
|
//
|
||
|
|
||
|
if (FindKpasswd)
|
||
|
{
|
||
|
ServerList = &MitRealm->KpasswdNames;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ServerList = &MitRealm->KdcNames;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we aren't forcing rediscovery, use the last known KDC
|
||
|
//
|
||
|
if (ServerList->ServerCount <= 0)
|
||
|
{
|
||
|
Status = STATUS_NO_LOGON_SERVERS;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
if ((DesiredFlags & DS_FORCE_REDISCOVERY) == 0)
|
||
|
{
|
||
|
ServerIndex = ServerList->LastServerUsed;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// If we are forcing rediscovery, try the next realm in the list
|
||
|
//
|
||
|
|
||
|
#ifndef WIN32_CHICAGO // Implement using a global & cs
|
||
|
ServerIndex = InterlockedExchangeAdd(&ServerList->LastServerUsed, 1) + 1;
|
||
|
#else // WIN32_CHICAGO
|
||
|
ServerIndex = ServerList->LastServerUsed + 1;
|
||
|
(ServerList->LastServerUsed) ++;
|
||
|
#endif // WIN32_CHICAGO // Implement using a global & cs
|
||
|
if (ServerIndex >= ServerList->ServerCount)
|
||
|
{
|
||
|
InterlockedExchange(&ServerList->LastServerUsed, 0);
|
||
|
}
|
||
|
}
|
||
|
ServerIndex = ServerIndex % ServerList->ServerCount;
|
||
|
|
||
|
KdcNameString = ServerList->ServerNames[ServerIndex];
|
||
|
AddressType = DS_UNKNOWN_ADDRESS_TYPE;
|
||
|
Status = STATUS_SUCCESS;
|
||
|
}
|
||
|
//
|
||
|
// If we haven't yet found a KDC try DsGetDcName
|
||
|
//
|
||
|
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
//
|
||
|
// We are dependent on AFD, so wait for it to start
|
||
|
//
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
if (!KerbAfdStarted)
|
||
|
{
|
||
|
Status = KerbWaitForService(L"LanmanWorkstation", NULL, KerbGlobalKdcWaitTime);
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
DebugLog((DEB_WARN, "Failed to wait forAFD: 0x%x\n",Status));
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
KerbAfdStarted = TRUE;
|
||
|
|
||
|
//
|
||
|
// Setup for network change initialization.
|
||
|
//
|
||
|
KerbInitNetworkChangeEvent();
|
||
|
}
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
//
|
||
|
// Build the null-terminated domain name.
|
||
|
//
|
||
|
|
||
|
DomainName = (LPWSTR) KerbAllocate(RealmName->Length + sizeof(WCHAR));
|
||
|
if (DomainName == NULL)
|
||
|
{
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(
|
||
|
DomainName,
|
||
|
RealmName->Buffer,
|
||
|
RealmName->Length
|
||
|
);
|
||
|
DomainName[RealmName->Length/sizeof(WCHAR)] = L'\0';
|
||
|
|
||
|
|
||
|
//
|
||
|
// If a principal name was provided, pass it to Netlogon
|
||
|
//
|
||
|
|
||
|
if ((PrincipalName != NULL) && (PrincipalName->Length != 0))
|
||
|
{
|
||
|
AccountName = (LPWSTR) KerbAllocate(PrincipalName->Length + sizeof(WCHAR));
|
||
|
if (AccountName == NULL)
|
||
|
{
|
||
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
RtlCopyMemory(
|
||
|
AccountName,
|
||
|
PrincipalName->Buffer,
|
||
|
PrincipalName->Length
|
||
|
);
|
||
|
AccountName[PrincipalName->Length/sizeof(WCHAR)] = L'\0';
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Actual flags that will be used.
|
||
|
//
|
||
|
|
||
|
if (DesiredFlags & DS_PDC_REQUIRED)
|
||
|
{
|
||
|
ActualFlags = (DesiredFlags | KERB_LOCATOR_FLAGS) & ~DS_KDC_REQUIRED;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ActualFlags = DesiredFlags | KERB_LOCATOR_FLAGS;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Find the name of a DC in this domain:
|
||
|
//
|
||
|
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
|
||
|
//
|
||
|
// Note: Watch out for changes in the enumeration below.
|
||
|
//
|
||
|
|
||
|
if ( !KerbNetlogonStarted &&
|
||
|
( Role > KerbRoleStandalone ) &&
|
||
|
( fRebootedSinceJoin ))
|
||
|
{
|
||
|
|
||
|
Status = KerbWaitForService(
|
||
|
SERVICE_NETLOGON,
|
||
|
NETLOGON_STARTED_EVENT,
|
||
|
KerbGlobalKdcWaitTime
|
||
|
);
|
||
|
if (NT_SUCCESS(Status))
|
||
|
{
|
||
|
KerbNetlogonStarted = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Status = STATUS_SUCCESS;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (KerbNetlogonStarted)
|
||
|
{
|
||
|
CalledNetlogonDirectly = TRUE;
|
||
|
|
||
|
NetStatus = DsrGetDcNameEx2(
|
||
|
NULL,
|
||
|
AccountName, // no account name
|
||
|
UF_ACCOUNT_TYPE_MASK, // any account type
|
||
|
DomainName,
|
||
|
NULL, // no domain GUID
|
||
|
NULL, // no site GUID,
|
||
|
ActualFlags,
|
||
|
&DcInfo
|
||
|
);
|
||
|
|
||
|
}
|
||
|
else
|
||
|
#endif // WIN32_CHICAGO
|
||
|
{
|
||
|
TryNetapi:
|
||
|
NetStatus = DsGetDcNameW(
|
||
|
NULL,
|
||
|
DomainName,
|
||
|
NULL, // no domain GUID
|
||
|
NULL, // no site GUID,
|
||
|
ActualFlags,
|
||
|
&DcInfo
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (NetStatus != NO_ERROR)
|
||
|
{
|
||
|
if (NetStatus == STATUS_NETLOGON_NOT_STARTED)
|
||
|
{
|
||
|
KerbNetlogonStarted = FALSE;
|
||
|
if (CalledNetlogonDirectly)
|
||
|
{
|
||
|
CalledNetlogonDirectly = FALSE;
|
||
|
goto TryNetapi;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
DebugLog((
|
||
|
DEB_WARN,
|
||
|
"No MS DC for domain %ws, account name %ws, locator flags 0x%x: %d. %ws, line %d\n",
|
||
|
DomainName,
|
||
|
(AccountName ? AccountName :L"NULL"),
|
||
|
ActualFlags,
|
||
|
NetStatus,
|
||
|
THIS_FILE,
|
||
|
__LINE__
|
||
|
));
|
||
|
|
||
|
if (NetStatus == ERROR_NETWORK_UNREACHABLE)
|
||
|
{
|
||
|
Status = STATUS_NETWORK_UNREACHABLE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( Role == KerbRoleRealmlessWksta )
|
||
|
{
|
||
|
DebugLog((DEB_TRACE, "NEG Caching realm %wZ\n", RealmName));
|
||
|
KerbCacheBinding(
|
||
|
RealmName,
|
||
|
&KdcNameString,
|
||
|
AddressType,
|
||
|
DesiredFlags,
|
||
|
DcFlags,
|
||
|
( CacheFlags | KERB_BINDING_NEGATIVE_ENTRY ),
|
||
|
BindingCacheEntry
|
||
|
);
|
||
|
}
|
||
|
|
||
|
Status = STATUS_NO_LOGON_SERVERS;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else // no error
|
||
|
{
|
||
|
|
||
|
RtlInitUnicodeString(
|
||
|
&KdcNameString,
|
||
|
DcInfo->DomainControllerAddress+2
|
||
|
);
|
||
|
|
||
|
AddressType = DcInfo->DomainControllerAddressType;
|
||
|
DcFlags |= DcInfo->Flags;
|
||
|
Status = STATUS_SUCCESS;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
DebugLog((
|
||
|
DEB_ERROR,
|
||
|
"No DC for domain %ws, account name %ws, locator flags 0x%x: %d. %ws, line %d\n",
|
||
|
DomainName,
|
||
|
(AccountName ? AccountName :L"NULL"),
|
||
|
ActualFlags,
|
||
|
NetStatus,
|
||
|
THIS_FILE,
|
||
|
__LINE__
|
||
|
));
|
||
|
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Make a binding
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// If this is a local call so don't bother with the socket
|
||
|
//
|
||
|
|
||
|
Status = KerbCacheBinding(
|
||
|
RealmName,
|
||
|
&KdcNameString,
|
||
|
AddressType,
|
||
|
DesiredFlags, // Flags that we would like to have
|
||
|
DcFlags, // This is what the dc has
|
||
|
CacheFlags, // Special kerberos flags so we don't use
|
||
|
// locator's bit space
|
||
|
BindingCacheEntry
|
||
|
);
|
||
|
|
||
|
if (!NT_SUCCESS(Status))
|
||
|
{
|
||
|
goto Cleanup;
|
||
|
}
|
||
|
|
||
|
|
||
|
Cleanup:
|
||
|
if (DomainName != NULL)
|
||
|
{
|
||
|
KerbFree(DomainName);
|
||
|
}
|
||
|
|
||
|
if (AccountName != NULL)
|
||
|
{
|
||
|
KerbFree(AccountName);
|
||
|
}
|
||
|
#ifndef WIN32_CHICAGO
|
||
|
if (DcInfo != NULL)
|
||
|
{
|
||
|
if (CalledNetlogonDirectly)
|
||
|
{
|
||
|
I_NetLogonFree(DcInfo);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NetApiBufferFree(DcInfo);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KerbFreeString( &LocalMachineName );
|
||
|
#endif // WIN32_CHICAGO
|
||
|
|
||
|
return(Status);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|