Leaked source code of windows server 2003
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.
 
 
 
 
 
 

4120 lines
124 KiB

/*++
Copyright (c) 1997-1998 Microsoft Corporation
Module Name:
message.c
Abstract:
This module contains the code to process a BINL request message
for the BINL server.
Author:
Colin Watson (colinw) 2-May-1997
Environment:
User Mode - Win32
Revision History:
--*/
#include "binl.h"
#pragma hdrstop
#if DBG
DWORD BinlRepeatSleep;
#endif
const WCHAR IntelOSChooser[] = L"OSChooser\\i386\\startrom.com";
const WCHAR IA64OSChooser[] = L"OSChooser\\ia64\\oschoice.efi";
WCHAR DefaultNamingContext[] = L"defaultNamingContext";
// Connection information to a DC in our domain
PLDAP DCLdapHandle = NULL;
PWCHAR * DCBase = NULL;
// Connection information to the Global Catalog for our enterprise
PLDAP GCLdapHandle = NULL;
PWCHAR * GCBase = NULL;
DWORD
GetGuidFromPacket(
LPDHCP_MESSAGE DhcpReceiveMessage,
LPDHCP_SERVER_OPTIONS DhcpOptions,
OUT PUCHAR Guid,
OUT PDWORD GuidLength OPTIONAL,
OUT PMACHINE_INFO *MachineInfo
);
LPOPTION
AppendClientRequestedParameters(
DHCP_IP_ADDRESS IpAddress,
DHCP_IP_ADDRESS SubnetMask,
LPBYTE RequestedList,
DWORD ListLength,
LPOPTION Option,
LPBYTE OptionEnd,
CHAR *ClassIdentifier,
DWORD ClassIdentifierLength,
BOOL fSwitchedSubnet
);
DWORD
RecognizeClient(
PUCHAR pGuid,
PMACHINE_INFO * pMachineInfo,
DWORD dwRequestedInfo,
ULONG SecondsSinceBoot,
USHORT SystemArchitecture
);
DWORD
GetBootParametersExt(
PMACHINE_INFO pMachineInfo,
DWORD dwRequestedInfo,
USHORT SystemArchitecture,
BOOL fGlobal);
VOID
FreeConnection(
PLDAP * LdapHandle,
PWCHAR ** Base
);
DWORD
ProcessMessage(
LPBINL_REQUEST_CONTEXT RequestContext
)
/*++
Routine Description:
This function dispatches the processing of a received BINL message.
The handler functions will create the response message if necessary.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
Return Value:
Windows Error.
--*/
{
DWORD Error;
BOOL fSendResponse,
fSubnetsListEmpty,
fReadyToTerminate,
fAllThreadsBusy;
DHCP_SERVER_OPTIONS dhcpOptions;
LPDHCP_MESSAGE binlReceiveMessage;
TraceFunc("ProcessMessage( )\n" );
//
// Simply ignore messages when the service is paused.
//
if( BinlGlobalServiceStatus.dwCurrentState == SERVICE_PAUSED )
{
Error = ERROR_BINL_SERVICE_PAUSED;
goto t_done;
}
binlReceiveMessage = (LPDHCP_MESSAGE)RequestContext->ReceiveBuffer;
//
// If it is an OSChooser message, then process that separately
// since they don't conform to the DHCP layout. This will send
// any messages that it needs to.
//
if (binlReceiveMessage->Operation == OSC_REQUEST)
{
Error = OscProcessMessage(RequestContext);
goto t_done;
}
RtlZeroMemory( &dhcpOptions, sizeof( dhcpOptions ) );
//BinlDumpMessage(DEBUG_MESSAGE, binlReceiveMessage);
Error = ExtractOptions(
binlReceiveMessage,
&dhcpOptions,
RequestContext->ReceiveMessageSize );
if( Error != ERROR_SUCCESS ) {
goto t_done;
}
if (!dhcpOptions.MessageType) {
goto t_done; // BOOTP request
}
#if 0
if (dhcpOptions.SystemArchitecture
!= DHCP_OPTION_CLIENT_ARCHITECTURE_X86) {
BinlPrintDbg((
DEBUG_OPTIONS,
"ProcessMessage: Client ignored - unsupported architecture type %d \n",
dhcpOptions.SystemArchitecture ) );
goto t_done;
}
#endif
if ( ( !AnswerRequests ) &&
( RequestContext->ActiveEndpoint->Port == DHCP_SERVR_PORT )) {
//
// this is not the 4011 port, therefore it must be the DHCP port.
// We're configured to not answer requests on this port right now
// therefore we'll toss this packet.
//
BinlPrint((DEBUG_OPTIONS, "Client ignored - Not answering requests (AnswerRequests == FALSE)\n" ));
goto t_done;
}
if (BinlGlobalAuthorized == FALSE) {
BinlPrint((DEBUG_ROGUE, "BINL has not passed rogue detection. Ignoring packet.\n" ));
//
// We'll possibly log an event here since we don't log an event
// at startup saying what our rogue state is.
//
LogCurrentRogueState( TRUE );
goto t_done;
}
//
// Dispatch based on Message Type
//
RequestContext->MessageType = *dhcpOptions.MessageType;
switch( *dhcpOptions.MessageType ) {
case DHCP_DISCOVER_MESSAGE:
Error = ProcessBinlDiscover( RequestContext, &dhcpOptions );
fSendResponse = TRUE;
break;
case DHCP_INFORM_MESSAGE:
Error = ProcessBinlInform( RequestContext, &dhcpOptions );
fSendResponse = TRUE;
break;
case DHCP_REQUEST_MESSAGE:
Error = ProcessBinlRequest( RequestContext, &dhcpOptions );
fSendResponse = TRUE;
break;
default:
BinlPrintDbg(( DEBUG_STOC,
"Received a invalid message type, %ld.\n",
*dhcpOptions.MessageType ));
Error = ERROR_BINL_INVALID_BINL_MESSAGE;
break;
}
if ( ERROR_SUCCESS == Error && fSendResponse )
{
/*
BinlDumpMessage(
DEBUG_MESSAGE,
(LPDHCP_MESSAGE)RequestContext->SendBuffer
);
*/
BinlSendMessage( RequestContext );
}
t_done:
//
// delete the context structure for this thread
//
BinlFreeMemory( RequestContext->ReceiveBuffer );
BinlFreeMemory( RequestContext->SendBuffer );
BinlFreeMemory( RequestContext );
EnterCriticalSection( &g_ProcessMessageCritSect );
//
// Check to see if all worker threads were busy
//
fAllThreadsBusy = ( g_cProcessMessageThreads ==
g_cMaxProcessingThreads );
--g_cProcessMessageThreads;
//
// Check to see if this is the last worker thread
//
fReadyToTerminate = !g_cProcessMessageThreads;
LeaveCriticalSection( &g_ProcessMessageCritSect );
//
// If all the worker threads were busy, then BinlProcessingLoop
// is waiting for a thread to complete. Set BinlGlobalRecvEvent
// so BinlProcessingLoop can continue.
//
if ( fAllThreadsBusy )
{
BinlPrintDbg( ( DEBUG_STOC,
"ProcessMessage: Alerting BinlProcessingLoop\n" )
);
SetEvent( BinlGlobalRecvEvent );
}
if ( fReadyToTerminate &&
WaitForSingleObject( BinlGlobalProcessTerminationEvent,
0 ) == WAIT_OBJECT_0 )
{
//
// there are no other ProcessMessage threads running, and
// the service is waiting to shutdown.
//
BinlPrintDbg( (DEBUG_MISC,
"ProcessMessage: shutdown complete.\n" )
);
BinlAssert( g_hevtProcessMessageComplete );
SetEvent( g_hevtProcessMessageComplete );
}
//
// thread exit
//
BinlPrintDbg( ( DEBUG_STOC,
"ProcessMessage exited\n" )
);
return Error;
}
DWORD
GetGuidFromPacket(
LPDHCP_MESSAGE DhcpReceiveMessage,
LPDHCP_SERVER_OPTIONS DhcpOptions,
OUT PUCHAR Guid,
OUT PDWORD GuidLength OPTIONAL,
OUT PMACHINE_INFO *MachineInfo
)
/*++
Routine Description:
This routine reads the guid from the client's packet (they send
us their guid). After obtaining their guid, we attempt to
recognize the client by querying the DS with the guid. If
found, MachineInfo will be returned with the information
requested, otherwise if we are accepting new clients we
may create the entry ourselves. otherwise, we just fail.
Arguments:
DhcpReceiveMessage - a pointer to the message received from the client
DhcpOptions - a pointer to the DHCP options gotton from the end of the
message the client sent
Guid - a pointer to memory for the guid to be copied to
GuidLength - a pointer to a dword to copy the guid length to
MachineInfo - a pointer to memory for us to client machine
information to. if we fail, this could be
returned as null.
Return Value:
Windows Error.
--*/
{
DWORD gLength = BINL_GUID_LENGTH;
const LONG AllFs[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
const LONG AllZeros[] = { 0x0, 0x0, 0x0, 0x0 };
DWORD err;
ULONG SecondsSinceBoot;
DWORD bytesToCopy = 0;
TraceFunc("GetGuidFromPacket( )\n" );
BinlAssert(sizeof(AllZeros) == BINL_GUID_LENGTH );
BinlAssert(sizeof(AllFs) == BINL_GUID_LENGTH);
if (DhcpOptions->GuidLength == 0) {
useNicAddress:
memset(Guid, 0x0, BINL_GUID_LENGTH);
if (DhcpReceiveMessage->HardwareAddressLength > BINL_GUID_LENGTH) {
bytesToCopy = BINL_GUID_LENGTH;
} else {
bytesToCopy = DhcpReceiveMessage->HardwareAddressLength;
}
memcpy(Guid + BINL_GUID_LENGTH - bytesToCopy,
DhcpReceiveMessage->HardwareAddress,
bytesToCopy
);
} else {
if (DhcpOptions->GuidLength > BINL_GUID_LENGTH) {
memcpy(Guid, DhcpOptions->Guid + DhcpOptions->GuidLength - BINL_GUID_LENGTH, BINL_GUID_LENGTH);
} else {
gLength = DhcpOptions->GuidLength;
memcpy(Guid, DhcpOptions->Guid, gLength);
}
if (!memcmp(Guid, (PUCHAR)AllFs, BINL_GUID_LENGTH) ||
!memcmp(Guid, (PUCHAR)AllZeros, BINL_GUID_LENGTH)) {
//
// if they specified all 00s or all FFs, use the NIC address.
//
goto useNicAddress;
}
}
if (GuidLength) {
*GuidLength = (bytesToCopy) ? bytesToCopy : gLength;
}
//
// we return STATUS_SUCCESS if we can handle this client.
//
// If a cache entry is found here, then it will be marked as InProgress as
// a side effect of finding it. We need to call BinlDoneWithCacheEntry
// when we're done with the entry.
//
// SecondsSinceBoot may have been sent on the network in network order.
// To correct this we assume the lower of the two bytes is the high byte.
// So if the high byte is more than the low one, we flip them.
//
SecondsSinceBoot = DhcpReceiveMessage->SecondsSinceBoot;
if ((SecondsSinceBoot >> 8) > (SecondsSinceBoot % 256)) {
SecondsSinceBoot = (SecondsSinceBoot >> 8) +
((SecondsSinceBoot % 256) << 8);
}
err = RecognizeClient( Guid,
MachineInfo,
MI_HOSTNAME | MI_BOOTFILENAME,
SecondsSinceBoot,
DhcpOptions->SystemArchitecture );
if ( err == ERROR_BINL_INVALID_GUID ) {
PWCHAR pwch;
WCHAR Buffer[6];
//
// Log an event with the hardware address of the offending client
//
pwch = (PWCHAR)BinlAllocateMemory( (( (sizeof(Buffer)/sizeof(Buffer[0])) - 1 ) *
DhcpReceiveMessage->HardwareAddressLength + 1 ) *
sizeof(WCHAR));
if (pwch != NULL) {
INT i;
*pwch = UNICODE_NULL;
for (i=0 ; i < DhcpReceiveMessage->HardwareAddressLength; i++) {
swprintf(Buffer, L" 0x%2x", (ULONG)(DhcpReceiveMessage->HardwareAddress[i]));
wcscat(pwch, Buffer);
}
BinlReportEventW(EVENT_SERVER_CLIENT_WITHOUT_GUID,
EVENTLOG_INFORMATION_TYPE,
1,
0,
&pwch,
NULL
);
BinlFreeMemory( pwch );
}
}
return err;
}
DWORD
ProcessBinlDiscoverInDhcp(
LPDHCP_MESSAGE DhcpReceiveMessage,
LPDHCP_SERVER_OPTIONS DhcpOptions
)
/*++
Routine Description:
this is a callback routine for a binl discover
will call GetGuidFromPacket which is start the
discovery process for the client.
Arguments:
DhcpReceiveMessage - a pointer to the packet received from
the client.
dhcpOptions - options extracted from the end of the request
Return Value:
Windows Error.
--*/
{
DWORD Error;
UCHAR Guid[BINL_GUID_LENGTH];
PMACHINE_INFO machineInfo = NULL;
TraceFunc("ProcessBinlDiscoverInDhcp( )\n" );
if ( !AnswerRequests ) {
BinlPrint((DEBUG_OPTIONS, "Client ignored - Not answering requests (AnswerRequests == FALSE)\n" ));
return ERROR_BINL_INVALID_BINL_CLIENT;
}
if (BinlGlobalAuthorized == FALSE) {
BinlPrint((DEBUG_ROGUE, "BINL has not passed rogue detection. Ignoring packet.\n" ));
//
// We'll possibly log an event here since we don't log an event
// at startup saying what our rogue state is.
//
LogCurrentRogueState( TRUE );
return ERROR_BINL_INVALID_BINL_CLIENT;
}
//
// If a cacheEntry is found here, then it will be marked as InProgress as
// a side effect of finding it. We need to call BinlDoneWithCacheEntry
// when we're done with the entry.
//
Error = GetGuidFromPacket( DhcpReceiveMessage,
DhcpOptions,
Guid,
NULL,
&machineInfo
);
if (machineInfo != NULL) {
BinlDoneWithCacheEntry( machineInfo, FALSE );
}
if( Error != ERROR_SUCCESS ) {
BinlPrint(( DEBUG_STOC, "BinlDiscover failed with Dhcp server, 0x%x\n", Error ));
}
return( Error );
}
DWORD
ProcessBinlDiscover(
LPBINL_REQUEST_CONTEXT RequestContext,
LPDHCP_SERVER_OPTIONS DhcpOptions
)
/*++
Routine Description:
This function will create the response message if necessary.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
dhcpOptions - Interesting options extracted from the request.
Return Value:
Windows Error.
--*/
{
DWORD Error;
LPDHCP_MESSAGE dhcpReceiveMessage;
LPDHCP_MESSAGE dhcpSendMessage;
BYTE messageType;
LPOPTION Option;
LPBYTE OptionEnd;
PMACHINE_INFO pMachineInfo = NULL;
UCHAR Guid[ BINL_GUID_LENGTH ];
DHCP_IP_ADDRESS ipaddr;
TraceFunc("ProcessBinlDiscover( )\n" );
dhcpReceiveMessage = (LPDHCP_MESSAGE) RequestContext->ReceiveBuffer;
//
// get our ip address. later we will append this to the sender's
// message, that way all communication after this is unicast.
//
ipaddr = BinlGetMyNetworkAddress( RequestContext );
if ( ipaddr == 0 ) {
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto Cleanup;
}
//
// If the client specified a server identifier option, we should
// drop this packet unless the identified server is this one.
//
if ( DhcpOptions->Server != NULL ) {
if (*DhcpOptions->Server != ipaddr) {
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto Cleanup;
}
}
//
// If a cacheEntry is found here, then it will be marked as InProgress as
// a side effect of finding it. We need to call BinlDoneWithCacheEntry
// when we're done with the entry.
//
Error = GetGuidFromPacket( dhcpReceiveMessage,
DhcpOptions,
Guid,
NULL,
&pMachineInfo
);
if (Error != ERROR_SUCCESS) {
goto Cleanup;
}
//
// Generate and send a reply.
//
dhcpReceiveMessage->BootFileName[ BOOT_FILE_SIZE - 1 ] = '\0';
dhcpSendMessage = (LPDHCP_MESSAGE) RequestContext->SendBuffer;
RtlZeroMemory( RequestContext->SendBuffer, DHCP_SEND_MESSAGE_SIZE );
dhcpSendMessage->Operation = BOOT_REPLY;
dhcpSendMessage->TransactionID = dhcpReceiveMessage->TransactionID;
dhcpSendMessage->ClientIpAddress = dhcpReceiveMessage->ClientIpAddress;
dhcpSendMessage->YourIpAddress = dhcpReceiveMessage->YourIpAddress;
if (pMachineInfo != NULL && pMachineInfo->HostAddress != 0) {
dhcpSendMessage->BootstrapServerAddress = pMachineInfo->HostAddress;
} else {
dhcpSendMessage->BootstrapServerAddress = ipaddr;
}
dhcpSendMessage->RelayAgentIpAddress = dhcpReceiveMessage->RelayAgentIpAddress;
dhcpSendMessage->Reserved = dhcpReceiveMessage->Reserved;
dhcpSendMessage->HardwareAddressType = dhcpReceiveMessage->HardwareAddressType;
dhcpSendMessage->HardwareAddressLength = dhcpReceiveMessage->HardwareAddressLength;
RtlCopyMemory(dhcpSendMessage->HardwareAddress,
dhcpReceiveMessage->HardwareAddress,
dhcpReceiveMessage->HardwareAddressLength );
Option = &dhcpSendMessage->Option;
OptionEnd = (LPBYTE)dhcpSendMessage + DHCP_SEND_MESSAGE_SIZE;
Option = (LPOPTION) DhcpAppendMagicCookie( (LPBYTE) Option, OptionEnd );
//
// Append OPTIONS.
//
messageType = DHCP_OFFER_MESSAGE;
Option = DhcpAppendOption(
Option,
OPTION_MESSAGE_TYPE,
&messageType,
1,
OptionEnd
);
Option = DhcpAppendOption(
Option,
OPTION_SERVER_IDENTIFIER,
&ipaddr,
sizeof(ipaddr),
OptionEnd );
Option = DhcpAppendOption(
Option,
OPTION_CLIENT_CLASS_INFO,
"PXEClient",
9,
OptionEnd
);
//
// Finally, add client requested parameters.
//
if ( DhcpOptions->ParameterRequestList != NULL ) {
Option = AppendClientRequestedParameters(
0,
0,
DhcpOptions->ParameterRequestList,
DhcpOptions->ParameterRequestListLength,
Option,
OptionEnd,
DhcpOptions->ClassIdentifier,
DhcpOptions->ClassIdentifierLength,
FALSE
);
}
Option = DhcpAppendOption(
Option,
OPTION_END,
NULL,
0,
OptionEnd
);
RequestContext->SendMessageSize = (DWORD)((LPBYTE)Option - (LPBYTE)dhcpSendMessage);
BinlAssert( RequestContext->SendMessageSize <= DHCP_SEND_MESSAGE_SIZE );
Error = ERROR_SUCCESS;
Cleanup:
if ( pMachineInfo ) {
BinlDoneWithCacheEntry( pMachineInfo, FALSE );
}
if( Error != ERROR_SUCCESS ) {
BinlPrintDbg(( DEBUG_STOC, "!! Error 0x%08x - DhcpDiscover failed.\n", Error ));
}
return( Error );
}
DWORD
ProcessBinlRequestInDhcp(
LPDHCP_MESSAGE DhcpReceiveMessage,
LPDHCP_SERVER_OPTIONS DhcpOptions,
PCHAR HostName,
PCHAR BootFileName,
DHCP_IP_ADDRESS *BootstrapServerAddress,
LPOPTION *Option,
PBYTE OptionEnd
)
/*++
Routine Description:
This function will create the response message if necessary.
Arguments:
dhcpOptions - Interesting options extracted from the request.
Return Value:
Windows Error.
--*/
{
DWORD Error;
PMACHINE_INFO pMachineInfo = NULL;
BOOLEAN includePXE = TRUE;
DHCP_IP_ADDRESS ipaddr;
UCHAR Guid[BINL_GUID_LENGTH];
DWORD GuidLength;
TraceFunc("ProcessBinlRequestInDhcp( )\n" );
if ( !AnswerRequests ) {
BinlPrint((DEBUG_OPTIONS, "Client ignored - Not answering requests (AnswerRequests == FALSE)\n" ));
return ERROR_BINL_INVALID_BINL_CLIENT;
}
if (BinlGlobalAuthorized == FALSE) {
BinlPrint((DEBUG_ROGUE, "BINL has not passed rogue detection. Ignoring packet.\n" ));
//
// We'll possibly log an event here since we don't log an event
// at startup saying what our rogue state is.
//
LogCurrentRogueState( TRUE );
return ERROR_BINL_INVALID_BINL_CLIENT;
}
//
// If a cache entry is found here, then it will be marked as InProgress as
// a side effect of finding it. We need to call BinlDoneWithCacheEntry
// when we're done with the entry.
//
Error = GetGuidFromPacket( DhcpReceiveMessage,
DhcpOptions,
Guid,
&GuidLength,
&pMachineInfo
);
if (Error != ERROR_SUCCESS) {
goto Cleanup;
}
if (pMachineInfo->HostName == NULL) {
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto Cleanup;
}
if (!BinlUnicodeToAnsi(pMachineInfo->HostName,HostName,BOOT_SERVER_SIZE)) {
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto Cleanup;
}
if (!BinlUnicodeToAnsi(pMachineInfo->BootFileName,BootFileName,BOOT_FILE_SIZE)) {
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto Cleanup;
}
BinlPrintDbg(( DEBUG_MISC, "HostName: %s\n", HostName ));
BinlPrintDbg(( DEBUG_MISC, "BootFileName: %s\n", BootFileName ));
//
// if the server is our own, then the machineInfo->HostAddress will be
// 0 and the DHCP server will fill in the correct one for us so long as
// we return success.
//
memcpy( BootstrapServerAddress,
&pMachineInfo->HostAddress,
sizeof( DHCP_IP_ADDRESS ) );
if (DhcpOptions->GuidLength != 0) {
*Option = DhcpAppendOption(
*Option,
OPTION_CLIENT_GUID,
DhcpOptions->Guid,
DhcpOptions->GuidLength,
OptionEnd );
} else {
UCHAR TmpBuffer[17];
TmpBuffer[0] = '\0';
memcpy(TmpBuffer + 1, Guid, GuidLength);
*Option = DhcpAppendOption(
*Option,
OPTION_CLIENT_GUID,
TmpBuffer,
17,
OptionEnd );
}
//
// check if OPTION_CLIENT_CLASS_INFO is already specified, if so, then
// don't put PXEClient in again
//
if (DhcpOptions->ParameterRequestList != NULL) {
LPBYTE requestList = DhcpOptions->ParameterRequestList;
ULONG listLength = DhcpOptions->ParameterRequestListLength;
while (listLength > 0) {
if (*requestList == OPTION_CLIENT_CLASS_INFO) {
includePXE = FALSE;
break;
}
listLength--;
requestList++;
}
}
if (includePXE) {
*Option = DhcpAppendOption(
*Option,
OPTION_CLIENT_CLASS_INFO,
"PXEClient",
9,
OptionEnd
);
}
Error = ERROR_SUCCESS;
Cleanup:
if (pMachineInfo != NULL) {
BinlDoneWithCacheEntry( pMachineInfo, FALSE );
}
if( Error != ERROR_SUCCESS ) {
BinlPrintDbg(( DEBUG_STOC, "!! Error 0x%08x - BINL Request failed.\n", Error ));
}
return( Error );
}
DWORD
ProcessBinlRequest(
LPBINL_REQUEST_CONTEXT RequestContext,
LPDHCP_SERVER_OPTIONS DhcpOptions
)
/*++
Routine Description:
This function will create the response message if necessary.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
dhcpOptions - Interesting options extracted from the request.
Return Value:
Windows Error.
--*/
{
DWORD Error;
LPDHCP_MESSAGE dhcpReceiveMessage;
LPDHCP_MESSAGE dhcpSendMessage;
BYTE messageType;
LPOPTION Option;
LPBYTE OptionEnd;
PMACHINE_INFO pMachineInfo = NULL;
UCHAR Guid[ BINL_GUID_LENGTH ];
DHCP_IP_ADDRESS ipaddr;
DHCP_IP_ADDRESS boostrapIpAddr;
TraceFunc("ProcessBinlRequest( )\n" );
#if DBG
if ( BinlRepeatSleep )
{
BinlPrintDbg((DEBUG_STOC, "Delay response %u milliseconds.\n", BinlRepeatSleep ));
Sleep( BinlRepeatSleep );
BinlPrintDbg((DEBUG_STOC, "Awakening from sleep...\n" ));
}
#endif // DBG
dhcpReceiveMessage = (LPDHCP_MESSAGE) RequestContext->ReceiveBuffer;
//
// If the client specified a server identifier option, we should
// drop this packet unless the identified server is this one.
//
ipaddr = BinlGetMyNetworkAddress( RequestContext );
if ( ipaddr == 0 ) {
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto Cleanup;
}
if ( DhcpOptions->Server != NULL ) {
if ( *DhcpOptions->Server != ipaddr ) {
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto Cleanup;
}
}
//
// If a cacheEntry is found here, then it will be marked as InProgress as
// a side effect of finding it. We need to call BinlDoneWithCacheEntry
// when we're done with the entry.
//
Error = GetGuidFromPacket( dhcpReceiveMessage,
DhcpOptions,
Guid,
NULL,
&pMachineInfo
);
if (Error != ERROR_SUCCESS) {
goto Cleanup;
}
//
// Generate and send a reply.
//
dhcpReceiveMessage->BootFileName[ BOOT_FILE_SIZE - 1 ] = '\0';
dhcpSendMessage = (LPDHCP_MESSAGE) RequestContext->SendBuffer;
RtlZeroMemory( RequestContext->SendBuffer, DHCP_SEND_MESSAGE_SIZE );
dhcpSendMessage->Operation = BOOT_REPLY;
dhcpSendMessage->TransactionID = dhcpReceiveMessage->TransactionID;
dhcpSendMessage->ClientIpAddress = dhcpReceiveMessage->ClientIpAddress;
dhcpSendMessage->YourIpAddress = dhcpReceiveMessage->YourIpAddress;
dhcpSendMessage->RelayAgentIpAddress = dhcpReceiveMessage->RelayAgentIpAddress;
dhcpSendMessage->Reserved = dhcpReceiveMessage->Reserved;
dhcpSendMessage->HardwareAddressType = dhcpReceiveMessage->HardwareAddressType;
dhcpSendMessage->HardwareAddressLength = dhcpReceiveMessage->HardwareAddressLength;
RtlCopyMemory(dhcpSendMessage->HardwareAddress,
dhcpReceiveMessage->HardwareAddress,
min(dhcpReceiveMessage->HardwareAddressLength, sizeof(dhcpSendMessage->HardwareAddress)) );
if (pMachineInfo->HostName == NULL) {
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto Cleanup;
}
// Comparing BYTE count to CHAR count
BinlAssert( sizeof( dhcpSendMessage->HostName ) >= wcslen( pMachineInfo->HostName ) );
BinlAssert( sizeof( dhcpSendMessage->BootFileName ) >= wcslen( pMachineInfo->BootFileName ) );
if (!BinlUnicodeToAnsi(pMachineInfo->HostName,dhcpSendMessage->HostName,BOOT_SERVER_SIZE)) {
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto Cleanup;
}
if (!BinlUnicodeToAnsi(pMachineInfo->BootFileName,dhcpSendMessage->BootFileName,BOOT_FILE_SIZE)) {
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto Cleanup;
}
//
// if the machineinfo->HostAddress is zero, then that means the hostname
// is the same as ours. we therefore slap in our own ipaddress in.
//
boostrapIpAddr = pMachineInfo->HostAddress;
if (boostrapIpAddr == 0) {
boostrapIpAddr = ipaddr;
}
dhcpSendMessage->BootstrapServerAddress = boostrapIpAddr;
BinlPrintDbg(( DEBUG_MISC, "HostName: %s\n", dhcpSendMessage->HostName ));
BinlPrintDbg(( DEBUG_MISC, "HostAddress: %u.%u.%u.%u\n",
dhcpSendMessage->BootstrapServerAddress & 0xFF,
(dhcpSendMessage->BootstrapServerAddress >> 8) & 0xFF,
(dhcpSendMessage->BootstrapServerAddress >> 16) & 0xFF,
(dhcpSendMessage->BootstrapServerAddress >> 24) & 0xFF ));
BinlPrintDbg(( DEBUG_MISC, "BootFileName: %s\n", dhcpSendMessage->BootFileName ));
Option = &dhcpSendMessage->Option;
OptionEnd = (LPBYTE)dhcpSendMessage + DHCP_SEND_MESSAGE_SIZE;
Option = (LPOPTION) DhcpAppendMagicCookie( (LPBYTE) Option, OptionEnd );
//
// Append OPTIONS.
//
messageType = DHCP_ACK_MESSAGE;
Option = DhcpAppendOption(
Option,
OPTION_MESSAGE_TYPE,
&messageType,
1,
OptionEnd
);
Option = DhcpAppendOption(
Option,
OPTION_SERVER_IDENTIFIER,
&ipaddr,
sizeof(ipaddr),
OptionEnd );
if (DhcpOptions->GuidLength != 0) {
Option = DhcpAppendOption(
Option,
OPTION_CLIENT_GUID,
DhcpOptions->Guid,
(UCHAR)DhcpOptions->GuidLength,
OptionEnd );
} else {
UCHAR TmpBuffer[BINL_GUID_LENGTH + 1];
TmpBuffer[0] = '\0';
memcpy(TmpBuffer + 1, pMachineInfo->Guid, BINL_GUID_LENGTH);
Option = DhcpAppendOption(
Option,
OPTION_CLIENT_GUID,
TmpBuffer,
sizeof(TmpBuffer),
OptionEnd );
}
Option = DhcpAppendOption(
Option,
OPTION_CLIENT_CLASS_INFO,
"PXEClient",
9,
OptionEnd
);
//
// Finally, add client requested parameters.
//
if ( DhcpOptions->ParameterRequestList != NULL ) {
Option = AppendClientRequestedParameters(
0,
0,
DhcpOptions->ParameterRequestList,
DhcpOptions->ParameterRequestListLength,
Option,
OptionEnd,
DhcpOptions->ClassIdentifier,
DhcpOptions->ClassIdentifierLength,
FALSE
);
}
Option = DhcpAppendOption(
Option,
OPTION_END,
NULL,
0,
OptionEnd
);
RequestContext->SendMessageSize = (DWORD)((LPBYTE)Option - (LPBYTE)dhcpSendMessage);
BinlAssert( RequestContext->SendMessageSize <= DHCP_SEND_MESSAGE_SIZE );
Error = ERROR_SUCCESS;
Cleanup:
if ( pMachineInfo ) {
BinlDoneWithCacheEntry( pMachineInfo, FALSE );
}
if( Error != ERROR_SUCCESS ) {
BinlPrintDbg(( DEBUG_STOC, "!! Error 0x%08x - BINL Request failed.\n", Error ));
}
return( Error );
}
DWORD
ProcessBinlInform(
IN LPBINL_REQUEST_CONTEXT RequestContext,
IN LPDHCP_SERVER_OPTIONS DhcpOptions
)
/*++
Routine Description:
This function will create the response message to the inform packet iff
the query is asking for our domain name.
Arguments:
RequestContext - A pointer to the BinlRequestContext block for
this request.
dhcpOptions - Interesting options extracted from the request.
Return Value:
Windows Error.
--*/
{
DWORD Error;
LPDHCP_MESSAGE dhcpReceiveMessage;
LPDHCP_MESSAGE dhcpSendMessage;
LPOPTION Option;
LPBYTE OptionEnd;
PCHAR domain = NULL;
DHCP_IP_ADDRESS ipaddr;
TraceFunc("ProcessBinlInform( )\n" );
dhcpReceiveMessage = (LPDHCP_MESSAGE)RequestContext->ReceiveBuffer;
dhcpSendMessage = (LPDHCP_MESSAGE)RequestContext->SendBuffer;
ipaddr = BinlGetMyNetworkAddress( RequestContext );
if ( ipaddr == 0 ) {
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto exit_inform;
}
if ( ! DhcpOptions->DSDomainNameRequested ) {
BinlPrintDbg((DEBUG_STOC, "Ignoring inform as no domain name option present.\n"));
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto exit_inform;
}
domain = GetDhcpDomainName();
if (domain == NULL) {
BinlPrintDbg((DEBUG_STOC, "Couldn't get domain name!\n"));
Error = ERROR_BINL_INVALID_BINL_CLIENT;
goto exit_inform;
}
// if the client IP address is not zero, we may AV in dhcpssvc because
// it updates a global counter tracking informs. Always have this as 0.
Option = FormatDhcpInformAck( // Here come the actual formatting of the ack!
dhcpReceiveMessage,
dhcpSendMessage,
0, // on a ack to an inform query for name, IP address not needed.
ipaddr
);
OptionEnd = (LPBYTE)dhcpSendMessage + DHCP_SEND_MESSAGE_SIZE;
// our enterprise name was requested, append it
Option = DhcpAppendEnterpriseName(
Option,
domain,
OptionEnd
);
// also, make the server send out a broadcast: if someone is using a bad
// ipaddr, we should make sure we reach him
dhcpSendMessage->Reserved = dhcpReceiveMessage->Reserved = htons(DHCP_BROADCAST);
//
// Finally, add client requested parameters.
//
if ( DhcpOptions->ParameterRequestList != NULL ) {
Option = AppendClientRequestedParameters(
0,
0,
DhcpOptions->ParameterRequestList,
DhcpOptions->ParameterRequestListLength,
Option,
OptionEnd,
DhcpOptions->ClassIdentifier,
DhcpOptions->ClassIdentifierLength,
FALSE
);
}
Option = DhcpAppendOption(
Option,
OPTION_END,
NULL,
0,
OptionEnd
);
RequestContext->SendMessageSize = (DWORD)((LPBYTE)Option - (LPBYTE)dhcpSendMessage);
BinlAssert( RequestContext->SendMessageSize <= DHCP_SEND_MESSAGE_SIZE );
Error = ERROR_SUCCESS;
exit_inform:
if (domain != NULL) {
LocalFree( domain );
}
return Error;
}
LPOPTION
ConsiderAppendingOption(
DHCP_IP_ADDRESS IpAddress,
DHCP_IP_ADDRESS SubnetMask,
LPOPTION Option,
BYTE OptionType,
LPBYTE OptionEnd,
CHAR *ClassIdentifier,
DWORD ClassIdentifierLength,
BOOL fSwitchedSubnet
)
/*++
Routine Description:
This function conditionally appends an option value to a response
message. The option is appended if the server has a valid value
to append.
Arguments:
IpAddress - The IP address of the client.
SubnetMask - The subnet mask of the client.
Option - A pointer to the place in the message buffer to append the
option.
OptionType - The option number to consider appending.
OptionEnd - End of Option Buffer
Return Value:
A pointer to end of the appended data.
--*/
{
LPBYTE optionValue = NULL;
DWORD optionSize;
DWORD status;
DWORD dwUnused;
TraceFunc( "ConsiderAppendingOption( )\n" );
switch ( OptionType ) {
//
// Options already handled.
//
case OPTION_SUBNET_MASK:
case OPTION_REQUESTED_ADDRESS:
case OPTION_LEASE_TIME:
case OPTION_OK_TO_OVERLAY:
case OPTION_MESSAGE_TYPE:
case OPTION_RENEWAL_TIME:
case OPTION_REBIND_TIME:
case OPTION_CLIENT_CLASS_INFO:
case OPTION_VENDOR_SPEC_INFO:
//
// Options it is illegal to ask for.
//
case OPTION_PAD:
case OPTION_PARAMETER_REQUEST_LIST:
case OPTION_END:
// Options for DHCP server, not for BINL
case OPTION_ROUTER_ADDRESS:
BinlPrintDbg(( DEBUG_ERRORS,
"Unrecognized option %d\n", OptionType));
break;
default:
break;
}
return Option;
}
LPOPTION
AppendClientRequestedParameters(
DHCP_IP_ADDRESS IpAddress,
DHCP_IP_ADDRESS SubnetMask,
LPBYTE RequestedList,
DWORD ListLength,
LPOPTION Option,
LPBYTE OptionEnd,
CHAR *ClassIdentifier,
DWORD ClassIdentifierLength,
BOOL fSwitchedSubnet
)
/*++
Routine Description:
Arguments:
Return Value:
A pointer to the end of appended data.
--*/
{
while ( ListLength > 0) {
Option = ConsiderAppendingOption(
IpAddress,
SubnetMask,
Option,
*RequestedList,
OptionEnd,
ClassIdentifier,
ClassIdentifierLength,
fSwitchedSubnet
);
ListLength--;
RequestedList++;
}
return Option;
}
DWORD
RecognizeClient(
PUCHAR pGuid,
PMACHINE_INFO * ppMachineInfo,
DWORD dwRequestedInfo,
ULONG SecondsSinceBoot,
USHORT SystemArchitecture
)
/*++
Routine Description:
This function only return ERROR_SUCCESS if we need to process the message
from this client. It may optionally return a cache entry if we actually
go off to the DS to get the entry.
Arguments:
Guid - Client identifier, sent to us by them.
SecondsSinceBoot - from the client. If we don't know this client and
this value is small then maybe this client is owned by another BINL
server. Give the other server time to respond before we send
OSChooser.
This gets around the problem (mostly) of two BINL servers that are
talking to two different DCs with a replication delay between them
where the client gets sent OSCHOOSER multiple times.
Alas, if DHCP is running on the same box and we're multihomed, we
can't delay as that will force the client to go to 4011. If the
client does that, then we'll probably return the wrong address.
ppMachineInfo - what we found. May be null if we didn't actually go off to
the DS.
SystemArchitecture - architecture for the client
Return Value:
--*/
{
HKEY KeyHandle;
DWORD Error;
BinlAssertMsg(dwRequestedInfo == (MI_HOSTNAME | MI_BOOTFILENAME),
"!! You must modify RecognizeClient() to generate new data\n" );
BinlAssert(ppMachineInfo);
TraceFunc( "RecognizeClient( )\n" );
//
// Attempt to get the boot parameters. This might fail if
// the server can't handle any more clients.
//
if ( AnswerOnlyValidClients ) {
//
// if we're only responding to existing clients, then call off to
// the DS to get the info.
//
Error = GetBootParameters( pGuid,
ppMachineInfo,
dwRequestedInfo,
SystemArchitecture,
FALSE );
} else {
//
// if we are answering new clients but only if it's after a
// certain timeout, then call off to the DS to get the info.
//
// Allow OSCHOOSER as a valid response, since AnswerOnlyValidClients is FALSE
//
Error = GetBootParameters( pGuid,
ppMachineInfo,
dwRequestedInfo,
SystemArchitecture,
(BOOLEAN) (SecondsSinceBoot >= BinlMinDelayResponseForNewClients) );
}
if ( Error == ERROR_SUCCESS ) {
BinlPrint((DEBUG_OPTIONS, "Recognizing client.\n" ));
BinlAssert( *ppMachineInfo != NULL );
if ( (*ppMachineInfo)->MyClient == FALSE ) {
//
// the cache entry is telling us not to handle this client.
//
BinlPrint((DEBUG_OPTIONS, "Binl cache entry says not to respond.\n" ));
Error = ERROR_BINL_INVALID_BINL_CLIENT;
BinlDoneWithCacheEntry( *ppMachineInfo, FALSE );
*ppMachineInfo = NULL;
}
} else {
if ( AnswerOnlyValidClients ) {
BinlPrint((DEBUG_OPTIONS, "Client ignored - Not answering for unknown clients (AnswerOnlyValid TRUE)\n" ));
} else {
BinlPrint((DEBUG_OPTIONS, "Client ignored - Not answering requests from boot %u < %u\n",
SecondsSinceBoot,
BinlMinDelayResponseForNewClients
));
}
}
return Error;
}
DWORD
UpdateAccount(
PCLIENT_STATE ClientState,
PMACHINE_INFO pMachineInfo,
BOOL fCreate
)
/*++
Routine Description:
Create a new computer object. BINL must impersonate the client so that the
appropriate access checks are performed on the DS.
Arguments:
LdapHandle - User credentially created LDAP connection
pMachineInfo - Information to be used to populate the new MAO
Return Value:
Win32 error code or ERROR_SUCCESS.
--*/
{
WCHAR BootFilePath[MAX_PATH];
ULONG LdapError = LDAP_SUCCESS; // not returned
DWORD Error = ERROR_SUCCESS; // this is the returned ERROR_BINL code
ULONG iModCount, i,q;
ULONG LdapMessageId;
ULONG LdapMessageType;
PLDAPMessage LdapMessage = NULL;
BOOLEAN Impersonating = FALSE;
LDAP_BERVAL guid_attr_value;
PLDAP_BERVAL guid_attr_values[2];
LDAP_BERVAL password_attr_value;
PLDAP_BERVAL password_attr_values[2];
DWORD dwRequiredFlags = MI_SAMNAME
| MI_BOOTFILENAME
| MI_HOSTNAME
| MI_SETUPPATH
| MI_PASSWORD;
PWCHAR attr_values[6][2];
PLDAPMod ldap_mods[6];
LDAPMod SamAccountName;
LDAPMod ObjectTypeComputer;
LDAPMod FilePath;
LDAPMod SetupPathMod;
LDAPMod UserAccountControl;
LDAPMod UnicodePwd;
LDAPMod NicGuid;
BOOLEAN invalidateCache = FALSE;
BOOLEAN updateCache = FALSE;
TraceFunc( "UpdateAccount( )\n" );
//
// First impersonate the client.
//
Error = OscImpersonate(ClientState);
if (Error != ERROR_SUCCESS) {
BinlPrintDbg((DEBUG_ERRORS,
"UpdateAccount: OscImpersonate failed %lx\n", Error));
goto Cleanup;
}
tryagain:
Impersonating = TRUE;
//
// now initialize all of the properties we want to set on the MAO.
//
// Make sure we have all the information we need.
if ( ! (pMachineInfo->dwFlags & MI_MACHINEDN) || pMachineInfo->MachineDN == NULL ) {
BinlAssertMsg( 0, "Missing the Machine's DN" );
OscAddVariableA( ClientState, "SUBERROR", "MACHINEDN" );
Error = ERROR_BINL_MISSING_VARIABLE;
goto Cleanup;
}
BinlAssert( !fCreate || (( pMachineInfo->dwFlags & dwRequiredFlags ) == dwRequiredFlags ) );
#if DBG
// We must have both of these or none of these.
// meant to have the !!. casts the value twice, so it will be a 0 or a 1
BinlAssert( !(pMachineInfo->dwFlags & MI_HOSTNAME) == !(pMachineInfo->dwFlags & MI_BOOTFILENAME) );
#endif
iModCount = 0;
if ( AssignNewClientsToServer &&
(pMachineInfo->dwFlags & (MI_HOSTNAME | MI_BOOTFILENAME)) )
{
if ( _snwprintf( BootFilePath,
sizeof(BootFilePath) / sizeof(BootFilePath[0]),
L"%ws\\%ws",
pMachineInfo->HostName,
pMachineInfo->BootFileName
) < 0 ) {
Error = ERROR_BAD_PATHNAME;
goto Cleanup;
}
BootFilePath[MAX_PATH-1] = L'\0'; // throw in terminating null just to be safe
attr_values[2][0] = BootFilePath;
attr_values[2][1] = NULL;
FilePath.mod_op = 0;
FilePath.mod_type = L"netbootMachineFilePath";
FilePath.mod_values = attr_values[2];
ldap_mods[iModCount++] = &FilePath;
}
if ( pMachineInfo->dwFlags & MI_SETUPPATH ) {
attr_values[3][0] = pMachineInfo->SetupPath;
attr_values[3][1] = NULL;
SetupPathMod.mod_op = 0;
SetupPathMod.mod_type = L"netbootInitialization";
SetupPathMod.mod_values = attr_values[3];
ldap_mods[iModCount++] = &SetupPathMod;
}
if ( pMachineInfo->dwFlags & MI_GUID ) {
guid_attr_values[0] = &guid_attr_value;
guid_attr_values[1] = NULL;
guid_attr_value.bv_val = pMachineInfo->Guid;
guid_attr_value.bv_len = BINL_GUID_LENGTH;
NicGuid.mod_op = LDAP_MOD_BVALUES;
NicGuid.mod_type = L"netbootGUID";
NicGuid.mod_bvalues = guid_attr_values;
ldap_mods[iModCount++] = &NicGuid;
}
if ( fCreate && ( pMachineInfo->dwFlags & MI_SAMNAME ) ) {
attr_values[0][0] = pMachineInfo->SamName;
attr_values[0][1] = NULL;
SamAccountName.mod_op = 0;
SamAccountName.mod_type = L"sAMAccountName";
SamAccountName.mod_values = attr_values[0];
ldap_mods[iModCount++] = &SamAccountName;
}
attr_values[4][0] = L"4096"; // 0x1000 -- workstation trust account, enabled
attr_values[4][1] = NULL;
UserAccountControl.mod_op = 0;
UserAccountControl.mod_type = L"userAccountControl";
UserAccountControl.mod_values = attr_values[4];
ldap_mods[iModCount++] = &UserAccountControl;
//
// if we're creating the MAO, then we need to specify the object type
// as a computer object
//
if ( fCreate ) {
attr_values[1][0] = L"Computer";
attr_values[1][1] = NULL;
ObjectTypeComputer.mod_op = 0;
ObjectTypeComputer.mod_type = L"objectClass";
ObjectTypeComputer.mod_values = attr_values[1];
ldap_mods[iModCount++] = &ObjectTypeComputer;
}
//
// Set the operation type depending on the create or modify flag
//
for ( i = 0 ; i < iModCount; i++ )
{
if ( fCreate ) {
ldap_mods[i]->mod_op |= LDAP_MOD_ADD;
} else {
ldap_mods[i]->mod_op |= LDAP_MOD_REPLACE;
}
}
ldap_mods[iModCount] = NULL; // terminate list
//
// The properties are initialized, so now either create or modify the MAO.
//
if ( fCreate || iModCount ) {
if ( fCreate ) {
BinlPrintDbg((DEBUG_OSC, "UpdateAccount() Creating a new MAO\n" ));
#if DBG
for (q = 0;q < iModCount; q++) {
BinlPrintDbg(( DEBUG_OSC, "LDAP Prop %x: Type: %S Value: %S",
q,
ldap_mods[q]->mod_type,
*ldap_mods[q]->mod_vals.modv_strvals ));
}
#endif
//
// synchronously Create the object.
//
LdapMessageId = ldap_add( ClientState->AuthenticatedDCLdapHandle, pMachineInfo->MachineDN, ldap_mods );
if (LdapMessageId == -1) {
Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
LdapError = LdapGetLastError();
LogLdapError( EVENT_WARNING_LDAP_ADD_ERROR,
LdapError,
ClientState->AuthenticatedDCLdapHandle
);
BinlPrintDbg(( DEBUG_ERRORS,
"CreateAccount ldap_add failed %x\n", LdapError));
goto Cleanup;
}
LdapMessageType = ldap_result(
ClientState->AuthenticatedDCLdapHandle,
LdapMessageId,
LDAP_MSG_ALL,
&BinlLdapSearchTimeout,
&LdapMessage);
if (LdapMessageType != LDAP_RES_ADD) {
BinlPrintDbg(( DEBUG_ERRORS,
"CreateAccount ldap_result returned type %lx\n", LdapMessageType));
OscAddVariableA( ClientState, "SUBERROR", "Unexpected LDAP error" );
Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
goto Cleanup;
}
LdapError = ldap_result2error(
ClientState->AuthenticatedDCLdapHandle,
LdapMessage,
0);
if (LdapError != LDAP_SUCCESS) {
if ((LdapError != LDAP_ALREADY_EXISTS) && (LdapError != LDAP_INSUFFICIENT_RIGHTS)) {
Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
LogLdapError( EVENT_WARNING_LDAP_ADD_ERROR,
LdapError,
ClientState->AuthenticatedDCLdapHandle
);
BinlPrintDbg(( DEBUG_ERRORS, "!!LdapError 0x%08x - UpdateAccount ldap_add_s( ) failed\n", LdapError));
goto Cleanup;
} else {
BinlPrintDbg((DEBUG_OSC, "UpdateAccount() tried to create an existing account. Try again, but modify existing MAO.\n" ));
fCreate = FALSE;
goto tryagain;
}
}
updateCache = TRUE;
} else {
//
// We don't strictly need to reset the properties below, as the
// content under the MAO should be static. But it won't really
// hurt things to try to reset in case something does change.
//
// Note that the reset of these properties may not succeed because
// the user may not have permissions to modify the MAO, depending
// on how the admin locks things down. (The admin can use GPO to
// allow the user to create MAOs but not modify the objects.)
//
//
// asynchronously reset the properties
//
BinlPrintDbg((DEBUG_OSC, "UpdateAccount() updating existing MAO\n" ));
LdapMessageId = ldap_modify( ClientState->AuthenticatedDCLdapHandle, pMachineInfo->MachineDN, ldap_mods );
if (LdapMessageId == -1) {
Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
LdapError = LdapGetLastError();
LogLdapError( EVENT_WARNING_LDAP_MODIFY_ERROR,
LdapError,
ClientState->AuthenticatedDCLdapHandle
);
BinlPrintDbg(( DEBUG_ERRORS,
"UpdateAccount ldap_modify(userAccountControl) failed %x\n", LdapError));
goto Cleanup;
}
LdapMessageType = ldap_result(
ClientState->AuthenticatedDCLdapHandle,
LdapMessageId,
LDAP_MSG_ALL,
&BinlLdapSearchTimeout,
&LdapMessage);
if (LdapMessageType != LDAP_RES_MODIFY) {
BinlPrintDbg(( DEBUG_ERRORS,
"CreateAccount ldap_result returned type %lx\n", LdapMessageType));
OscAddVariableA( ClientState, "SUBERROR", "Unexpected LDAP error" );
Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
goto Cleanup;
}
LdapError = ldap_result2error(
ClientState->AuthenticatedDCLdapHandle,
LdapMessage,
0);
if (LdapError != LDAP_SUCCESS) {
LogLdapError( EVENT_WARNING_LDAP_MODIFY_ERROR,
LdapError,
ClientState->AuthenticatedDCLdapHandle
);
BinlPrintDbg(( DEBUG_ERRORS, "CreateAccount ldap_result2error failed %x\n", LdapError));
// if the user doesn't have the rights to change
// the properties then we'll just silently ignore the error
// (though we did just log an error for it).
if ( LdapError != LDAP_INSUFFICIENT_RIGHTS) {
Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
goto Cleanup;
}
LdapError = LDAP_SUCCESS;
}
updateCache = TRUE;
}
}
//
// if we've made it this far, we've got a MAO that's setup properly.
// Now we need to reset the account password so the domain join is
// somewhat secure.
//
if ( pMachineInfo->dwFlags & MI_PASSWORD ) {
#ifdef SET_PASSWORD_WITH_LDAP
iModCount = 0;
password_attr_values[0] = &password_attr_value;
password_attr_values[1] = NULL;
password_attr_value.bv_val = (PUCHAR) pMachineInfo->Password;
password_attr_value.bv_len = pMachineInfo->PasswordLength;
UnicodePwd.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; // you always "Add" the "unicodePwd"
UnicodePwd.mod_type = L"unicodePwd";
UnicodePwd.mod_bvalues = password_attr_values;
ldap_mods[iModCount++] = &UnicodePwd;
ldap_mods[iModCount] = NULL; // terminate list
LdapError = ldap_modify_s( ClientState->AuthenticatedDCLdapHandle, pMachineInfo->MachineDN, ldap_mods );
if (LdapError != LDAP_SUCCESS) {
LogLdapError( EVENT_WARNING_LDAP_MODIFY_ERROR,
LdapError,
ClientState->AuthenticatedDCLdapHandle
);
BinlPrintDbg(( DEBUG_ERRORS, "!!LdapError 0x%08x - UpdateAccount ldap_modify_s( ) failed\n", LdapError));
goto Cleanup;
}
#else
//
// At this point we depend on LdapMessage being valid, which will
// *not* be the case if we are only setting the password. This
// breaks machine replacement for the moment.
//
BinlAssert( LdapMessage != NULL );
Error = OscUpdatePassword(
ClientState,
pMachineInfo->SamName,
pMachineInfo->Password,
ClientState->AuthenticatedDCLdapHandle,
LdapMessage);
if (Error != ERROR_SUCCESS) {
goto Cleanup;
}
#endif
}
Cleanup:
//
// if the machine name was generated,
// then attempt to remove it from the queue
//
if (ClientState->fAutomaticMachineName) {
BinlPrintDbg((DEBUG_OSC, "UpdateAccount: removing generated name from Queued DS Names list\n" ));
Error = RemoveQueuedDSName(pMachineInfo->Name);
if (Error != ERROR_SUCCESS) {
BinlPrintDbg(( DEBUG_ERRORS, "RemoveQueuedDSName failed: 0x%x\n", Error));
if (Error == ERROR_NOT_FOUND) {
//
// TODO: RIS currently has no way to deal with this error
// so make it succeed
//
Error = ERROR_SUCCESS;
}
}
}
//
// Convert the LdapError to a ERROR_BINL and put the LdapError
// into SUBERROR.
//
if ( LdapError != LDAP_SUCCESS )
{
OscCreateLDAPSubError( ClientState, LdapError );
switch ( LdapError )
{
case LDAP_ALREADY_EXISTS:
Error = ERROR_BINL_DUPLICATE_MACHINE_NAME_FOUND;
break;
case LDAP_INVALID_DN_SYNTAX:
Error = ERROR_BINL_INVALID_OR_MISSING_OU;
break;
default:
Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
break;
}
}
if ( updateCache && ( pMachineInfo->dwFlags & MI_GUID ) ) {
//
// update the cached DS information so that it is current. We do
// this because if the account is created in a child domain, we still
// have the info cached (even if it hasn't replicated to the GC yet).
//
PMACHINE_INFO pCacheEntry = NULL;
BinlCreateOrFindCacheEntry( pMachineInfo->Guid, TRUE, &pCacheEntry );
invalidateCache = FALSE;
// we don't care about the error coming back, only if a record was found.
if (pCacheEntry != NULL) {
pCacheEntry->TimeCreated = GetTickCount();
pCacheEntry->MyClient = TRUE;
pCacheEntry->EntryExists = TRUE;
if (pCacheEntry != pMachineInfo) {
memcpy( &pCacheEntry->HostAddress,
&pMachineInfo->HostAddress,
sizeof(pMachineInfo->HostAddress));
if ( pMachineInfo->Name ) {
pCacheEntry->Name = BinlStrDup( pMachineInfo->Name );
if (!pCacheEntry->Name) {
goto noMemory;
}
pCacheEntry->dwFlags |= MI_NAME_ALLOC | MI_NAME;
}
if ( pMachineInfo->MachineDN ) {
pCacheEntry->MachineDN = BinlStrDup( pMachineInfo->MachineDN );
if (!pCacheEntry->MachineDN) {
goto noMemory;
}
pCacheEntry->dwFlags |= MI_MACHINEDN_ALLOC | MI_MACHINEDN;
}
if ( pMachineInfo->SetupPath ) {
pCacheEntry->SetupPath = BinlStrDup( pMachineInfo->SetupPath );
if (!pCacheEntry->SetupPath) {
goto noMemory;
}
pCacheEntry->dwFlags |= MI_SETUPPATH_ALLOC | MI_SETUPPATH;
}
if ( pMachineInfo->HostName ) {
pCacheEntry->HostName = BinlStrDup( pMachineInfo->HostName );
if (!pCacheEntry->HostName) {
goto noMemory;
}
pCacheEntry->dwFlags |= MI_HOSTNAME_ALLOC | MI_HOSTNAME;
}
if ( pMachineInfo->SamName ) {
pCacheEntry->SamName = BinlStrDup( pMachineInfo->SamName );
if (!pCacheEntry->SamName) {
goto noMemory;
}
pCacheEntry->dwFlags |= MI_SAMNAME_ALLOC | MI_SAMNAME;
}
if ( pMachineInfo->Domain ) {
pCacheEntry->Domain = BinlStrDup( pMachineInfo->Domain );
if (!pCacheEntry->Domain) {
noMemory:
invalidateCache = TRUE;
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
} else {
pCacheEntry->dwFlags |= MI_DOMAIN_ALLOC | MI_DOMAIN;
}
}
}
BinlDoneWithCacheEntry( pCacheEntry, invalidateCache );
}
}
if ( invalidateCache && ( pMachineInfo->dwFlags & MI_GUID ) ) {
//
// invalidate the cached DS information if we failed because it's stale.
//
PMACHINE_INFO pCacheEntry = NULL;
BinlCreateOrFindCacheEntry( pMachineInfo->Guid, FALSE, &pCacheEntry );
// we don't care about the error coming back, only if a record was found.
if ((pCacheEntry != NULL) &&
(pCacheEntry != pMachineInfo)) {
BinlDoneWithCacheEntry( pCacheEntry, TRUE );
}
}
if (LdapMessage != NULL) {
ldap_msgfree(LdapMessage);
}
if (Impersonating) {
OscRevert(ClientState);
}
return Error;
}
DWORD
BinlGenerateNewEntry(
DWORD dwRequestedInfo,
USHORT SystemArchitecture,
PMACHINE_INFO * ppMachineInfo )
/*++
Routine Description:
fills in ppMachineInfo for a new entry if we are currently
allowing new clients.
Arguments:
dwRequestedInfo - a bitmask telling us what parameters we're looking for
SystemArchitecture - architecture of the client
ppMachineInfo - gets filled in with information requested information
Return Value:
ERROR_SUCCESS when we succeed.
otherwise ERROR_BINL_INVALID_BINL_CLIENT if we are not allowing new clients
or ERROR_NO_MEMORY if a memory allocation failed
or ERROR_BINL_FAILED_TO_INITIALIZE_CLIENT if we did not fill in
all the information requested.
--*/
{
DWORD Error = ERROR_BINL_INVALID_BINL_CLIENT;
TraceFunc( "BinlGenerateNewEntry( ... )\n" );
if ( AllowNewClients ) {
BinlPrint(( DEBUG_OPTIONS, "Server allows new clients" ));
if ( ( LimitClients == FALSE ) ||
( CurrentClientCount < BinlMaxClients ) ) {
BinlPrint(( DEBUG_OPTIONS, " and the Server is generating the OS Chooser path response.\n" ));
if ( dwRequestedInfo & MI_HOSTNAME ) {
if ( (*ppMachineInfo)->dwFlags & MI_HOSTNAME_ALLOC ) {
BinlFreeMemory( (*ppMachineInfo)->HostName );
(*ppMachineInfo)->HostName = NULL;
(*ppMachineInfo)->dwFlags &= ~MI_HOSTNAME_ALLOC;
}
EnterCriticalSection( &gcsParameters );
(*ppMachineInfo)->HostName = BinlStrDup( BinlGlobalOurDnsName );
LeaveCriticalSection( &gcsParameters );
if (!(*ppMachineInfo)->HostName) {
return (ERROR_OUTOFMEMORY);
}
(*ppMachineInfo)->dwFlags |= MI_HOSTNAME_ALLOC;
(*ppMachineInfo)->dwFlags |= MI_HOSTNAME;
}
if ( dwRequestedInfo & MI_BOOTFILENAME ) {
ULONG ulSize;
PCWSTR OsChooserName = NULL;
switch ( SystemArchitecture ) {
case DHCP_OPTION_CLIENT_ARCHITECTURE_X86:
OsChooserName = IntelOSChooser;
ulSize = (wcslen(OsChooserName)+1)*sizeof(WCHAR);
break;
case DHCP_OPTION_CLIENT_ARCHITECTURE_IA64:
OsChooserName = IA64OSChooser;
ulSize = (wcslen(OsChooserName)+1)*sizeof(WCHAR);
break;
default:
BinlAssertMsg( FALSE, "UnsupportedArchitecture" );
}
if (OsChooserName) {
if ( (*ppMachineInfo)->dwFlags & MI_BOOTFILENAME_ALLOC ) {
BinlFreeMemory( (*ppMachineInfo)->BootFileName );
(*ppMachineInfo)->dwFlags &= ~MI_BOOTFILENAME_ALLOC;
}
(*ppMachineInfo)->BootFileName = BinlAllocateMemory( ulSize );
if ( !(*ppMachineInfo)->BootFileName ) {
return (ERROR_OUTOFMEMORY);
}
RtlZeroMemory((*ppMachineInfo)->BootFileName, ulSize);
wcscpy((*ppMachineInfo)->BootFileName, OsChooserName);
(*ppMachineInfo)->dwFlags |= MI_BOOTFILENAME | MI_BOOTFILENAME_ALLOC;
}
}
Error = ( ((*ppMachineInfo)->dwFlags & dwRequestedInfo ) == dwRequestedInfo ?
ERROR_SUCCESS :
ERROR_BINL_FAILED_TO_INITIALIZE_CLIENT );
} else {
BinlPrint(( DEBUG_OPTIONS, "... BUT the server has reached MaxClients (%u)\n", BinlMaxClients ));
}
} else {
BinlPrint((DEBUG_OPTIONS, "Server does not allow new clients (AllowNewClients == FALSE )\n" ));
}
return Error;
}
DWORD
GetBootParameters(
PUCHAR pGuid,
PMACHINE_INFO * ppMachineInfo,
DWORD dwRequestedInfo,
USHORT SystemArchitecture,
BOOL AllowOSChooser
)
/*++
Routine Description:
Use the Directory Service to lookup an entry for this machine using Guid as
the value to lookup.
If there is no entry for this machine then return oschooser, but only
if the AllowOSChooser flag is set.
If a cache entry is returned, then the cache entry has been marked
InProgress so we have to call BinlDoneWithCacheEntry when the caller
is done with it.
Arguments:
pGuid - Supplies the machine GUID
ppMachineInfo - gets filled in with what we discovered
dwRequestedInfo - a bitmask telling us what parameters we're looking for
SystemArchitecture - architecture of the client
AllowOSChooser - signifies that we're allowed to respond to the client with
the oschooser
Return Value:
ERROR_SUCCESS or ERROR_BINL_INVALID_BINL_CLIENT or other error.
--*/
{
DWORD Error = ERROR_SUCCESS;
BOOLEAN myClient = TRUE;
BOOLEAN entryExists = FALSE;
TraceFunc( "GetBootParameters( )\n" );
BinlAssert( ppMachineInfo );
{
LPGUID GuidPtr = (LPGUID) pGuid;
BinlPrint((DEBUG_MISC, "Client Guid: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
GuidPtr->Data1, GuidPtr->Data2, GuidPtr->Data3,
GuidPtr->Data4[0], GuidPtr->Data4[1], GuidPtr->Data4[2], GuidPtr->Data4[3],
GuidPtr->Data4[4], GuidPtr->Data4[5], GuidPtr->Data4[6], GuidPtr->Data4[7] ));
}
if ( ppMachineInfo == NULL ) {
return E_OUTOFMEMORY;
}
if (*ppMachineInfo == NULL) {
//
// See if we have any entries in the cache.
// This also mark any entry found as being used.
//
Error = BinlCreateOrFindCacheEntry( pGuid, TRUE, ppMachineInfo );
if ( Error != ERROR_SUCCESS ) {
//
// if some bizarre error occurred OR if the client simply wasn't
// found and we're not sending down OS Chooser, then return the
// error here as there's no reason to query the DS.
//
if ( (Error != ERROR_BINL_INVALID_BINL_CLIENT ) ||
(AllowOSChooser == FALSE) ) {
return Error;
}
}
}
// Do we have everything we need?
if ( ( Error == ERROR_SUCCESS ) &&
(((*ppMachineInfo)->dwFlags & dwRequestedInfo) == dwRequestedInfo )) {
BinlPrint((DEBUG_MISC, "cache hit: returning success without querying ds DS\n"));
return Error; // Yes, no need to hit the DS.
}
//
// Initially search for the Computer object in the same domain as ourselves.
// This should be quick (because we are probably on a DC) and likely to work
// most of the time because the network topology will usually match the domain
// structure. If that fails then we fall back to looking at the global catalog.
//
if ( Error != ERROR_BINL_INVALID_BINL_CLIENT ) {
Error = GetBootParametersExt(
*ppMachineInfo,
dwRequestedInfo,
SystemArchitecture,
FALSE);
if ( Error == ERROR_BINL_INVALID_BINL_CLIENT ) {
Error = GetBootParametersExt(
*ppMachineInfo,
dwRequestedInfo,
SystemArchitecture,
TRUE );
}
}
if ( Error == ERROR_BINL_INVALID_BINL_CLIENT ) {
//
// Backdoor for testing/overiding the DS.
//
// If the registry has the GUID of the client, it
// overrides all the DS settings and answers anyways.
//
// NOTE: AllowNewClients must be turned on for OSChooser to
// be sent down.
//
HKEY KeyHandle;
if (AllowOSChooser == TRUE) {
//
// if the client is not found in the DS and we're allowed to
// answer new clients, then send down OSCHOOSER to get the new
// client going.
//
BinlPrint((DEBUG_MISC, "generating a new entry because AllowOSChooser is TRUE...\n"));
Error = BinlGenerateNewEntry( dwRequestedInfo, SystemArchitecture, ppMachineInfo );
if ( Error != ERROR_SUCCESS ) {
myClient = FALSE;
}
} else {
//
// We're not answering because we didn't find the client
// record but the client's SecondsSinceBoot is less than
// BinlMinDelayResponseForNewClients.
//
myClient = FALSE;
BinlPrint((DEBUG_OPTIONS, "... OS Chooser is not an option at this time... waiting...\n" ));
}
}
//
// Determine the host servers IP address iff it's not our own machine.
//
if ((Error == ERROR_SUCCESS) &&
( (*ppMachineInfo)->dwFlags & MI_HOSTNAME )
&& ( (*ppMachineInfo)->HostAddress == 0 )
&& ( (*ppMachineInfo)->HostName )) {
EnterCriticalSection( &gcsParameters );
if ( (BinlGlobalOurDnsName != NULL) &&
(_wcsicmp( BinlGlobalOurDnsName, (*ppMachineInfo)->HostName ) != 0 )) {
PCHAR machineName;
PHOSTENT host;
ULONG myMachineNameLength;
PCHAR myMachineName;
ULONG machineNameLength;
myMachineNameLength = wcslen( BinlGlobalOurDnsName ) + 1;
myMachineName = BinlAllocateMemory ( myMachineNameLength );
if ( myMachineName != NULL ) {
if (!BinlUnicodeToAnsi(BinlGlobalOurDnsName,myMachineName,(USHORT)myMachineNameLength)) {
BinlFreeMemory(myMachineName);
myMachineName = NULL;
}
}
LeaveCriticalSection( &gcsParameters );
machineNameLength = wcslen((*ppMachineInfo)->HostName) + 1;
machineName = BinlAllocateMemory( machineNameLength );
//
// Only fill in the IP address if the server is different from our
// own machine. If we fail for any reason, we'll just end up using
// our own IP address.
//
if ((machineName != NULL) &&
BinlUnicodeToAnsi((*ppMachineInfo)->HostName, machineName, (USHORT)machineNameLength)) {
host = gethostbyname( machineName );
if (host != NULL) {
(*ppMachineInfo)->HostAddress = *(PDHCP_IP_ADDRESS)host->h_addr;
// Adding stuff for multi-home NIC
if (myMachineName != NULL) {
PHOSTENT myhost;
int i;
myhost = gethostbyname( myMachineName );
if (myhost != NULL) {
i=0;
while (((myhost->h_addr_list)[i]) != NULL) {
if ((*((PDHCP_IP_ADDRESS)((myhost->h_addr_list)[i])))
== (*ppMachineInfo)->HostAddress) {
//
// this is us, leave it as 0
//
(*ppMachineInfo)->HostAddress = (DHCP_IP_ADDRESS)0;
break;
}
i++;
}
}
}
} else {
Error = ERROR_HOST_UNREACHABLE;
myClient = FALSE;
entryExists = TRUE;
}
BinlFreeMemory( machineName );
} else {
if (machineName) {
BinlFreeMemory( machineName );
}
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
myClient = FALSE;
entryExists = TRUE;
}
if ( myMachineName != NULL ) {
BinlFreeMemory( myMachineName );
}
} else {
LeaveCriticalSection( &gcsParameters );
}
}
if (Error != ERROR_SUCCESS) {
//
// If we didn't find the record, then we mark it that we don't need
// to respond and it doesn't exist. We then mark that we're done with
// the entry since we're not passing it back to the caller.
//
(*ppMachineInfo)->MyClient = myClient;
(*ppMachineInfo)->EntryExists = entryExists;
BinlDoneWithCacheEntry( *ppMachineInfo, FALSE );
*ppMachineInfo = NULL;
} else {
//
// we've filled in the interesting fields, therefore mark that the
// entry has valid data.
//
(*ppMachineInfo)->MyClient = TRUE;
(*ppMachineInfo)->EntryExists = TRUE;
}
return Error;
}
DWORD
GetBootParametersExt(
PMACHINE_INFO pMachineInfo,
DWORD dwRequestedInfo,
USHORT SystemArchitecture,
BOOL fGlobalSearch)
/*++
Routine Description:
Use the Directory Service to lookup an entry for this machine using Guid as
the value to lookup.
If there is no entry for this machine then return oschooser
Arguments:
pMachineInfo - identifies the machine in the DS.
dwRequestedInfo - mask telling what information we should query
SystemArchitecture - architecture of the client
GlobalSearch - TRUE if GC should be used
Return Value:
ERROR_SUCCESS or ERROR_BINL_INVALID_BINL_CLIENT
--*/
{
DWORD dwErr = ERROR_BINL_INVALID_BINL_CLIENT;
PLDAP LdapHandle = NULL;
PWCHAR * Base;
DWORD LdapError;
DWORD entryCount;
DWORD ldapRetryLimit = 0;
PLDAPMessage LdapMessage = NULL;
PWCHAR * FilePath;
PWCHAR * FilePath2;
PLDAPMessage CurrentEntry;
WCHAR Filter[128];
WCHAR EscapedGuid[64];
// Paramters we want from the Computer Object
PWCHAR ComputerAttrs[7];
PDUP_GUID_DN dupDN;
TraceFunc( "GetBootParametersExt( )\n" );
pMachineInfo->dwFlags &= MI_ALL_ALLOC; // clear all but the ALLOC bits
// we get all the info, regardless of what was requested.
ComputerAttrs[0] = L"netbootMachineFilePath";
ComputerAttrs[1] = L"netbootInitialization";
ComputerAttrs[2] = L"sAMAccountName";
ComputerAttrs[3] = L"dnsHostName";
ComputerAttrs[4] = L"distinguishedName";
ComputerAttrs[5] = L"netbootSIFFile";
ComputerAttrs[6] = NULL;
BinlAssertMsg( !(dwRequestedInfo & MI_PASSWORD), "Can't get the machine's password!" );
// Build the filter to find the Computer object with this GUID
ldap_escape_filter_element(pMachineInfo->Guid, BINL_GUID_LENGTH, EscapedGuid, sizeof(EscapedGuid) );
//
// Dont' use ';binary' because win2k Active Directory isn't compatible with the
// binary tag.
//
wsprintf( Filter, L"(&(objectClass=computer)(netbootGUID=%ws))", EscapedGuid );
#if 0 && DBG
{
LPGUID GuidPtr = (LPGUID) &pMachineInfo->Guid;
BinlPrint((DEBUG_MISC, "Client Guid: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
GuidPtr->Data1, GuidPtr->Data2, GuidPtr->Data3,
GuidPtr->Data4[0], GuidPtr->Data4[1], GuidPtr->Data4[2], GuidPtr->Data4[3],
GuidPtr->Data4[4], GuidPtr->Data4[5], GuidPtr->Data4[6], GuidPtr->Data4[7] ));
}
#endif
RetryConnection:
dwErr = InitializeConnection( fGlobalSearch, &LdapHandle, &Base );
if ( dwErr != LDAP_SUCCESS ) {
BinlPrint((DEBUG_ERRORS,
"InitializeConnection failed, ec = %x\n",dwErr));
SetLastError( dwErr );
dwErr = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
goto e0;
}
LdapError = ldap_search_ext_sW(LdapHandle,
*Base,
LDAP_SCOPE_SUBTREE,
Filter,
ComputerAttrs,
FALSE,
NULL,
NULL,
&BinlLdapSearchTimeout,
0,
&LdapMessage);
if ( LdapError != LDAP_SUCCESS ) {
HandleLdapFailure( LdapError,
EVENT_WARNING_LDAP_SEARCH_ERROR,
fGlobalSearch,
&LdapHandle,
FALSE ); // don't have lock
if (LdapHandle == NULL) {
if (++ldapRetryLimit < LDAP_SERVER_DOWN_LIMIT) {
goto RetryConnection;
}
dwErr = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
SetLastError( dwErr );
goto e1;
}
BinlPrint((DEBUG_MISC,
"ldap_search_ext_s %ws failed, ec = %x\n",
Filter,
LdapError));
goto e1;
}
// Did we get a Computer Object?
entryCount = ldap_count_entries( LdapHandle, LdapMessage );
if ( entryCount == 0 ) {
BinlPrint((DEBUG_MISC,
"ldap_count_entries %ws returned 0 entries\n",
Filter ));
dwErr = ERROR_BINL_INVALID_BINL_CLIENT;
goto e1; // nope
}
else if ( entryCount == -1 ) {
//
// catch any errors
//
dwErr = LdapGetLastError();
HandleLdapFailure( dwErr,
EVENT_WARNING_LDAP_COUNT_ENTRIES_ERROR,
fGlobalSearch,
&LdapHandle,
FALSE ); // don't have the lock
if (LdapHandle == NULL) {
if (++ldapRetryLimit < LDAP_SERVER_DOWN_LIMIT) {
goto RetryConnection;
}
SetLastError( dwErr );
}
BinlPrint((DEBUG_MISC, "ldap_count_entries %ws failed with error 0x%x\n",
Filter,
dwErr ));
goto e1;
}
// if we get more than more entry back, we will use only the
// first one.
CurrentEntry = ldap_first_entry( LdapHandle, LdapMessage );
if (entryCount > 1) {
BinlLogDuplicateDsRecords( (LPGUID)&pMachineInfo->Guid, LdapHandle, LdapMessage, CurrentEntry );
}
FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"distinguishedName");
if ( FilePath ) {
if ( pMachineInfo->dwFlags & MI_MACHINEDN_ALLOC ) {
BinlFreeMemory( pMachineInfo->MachineDN );
pMachineInfo->dwFlags &= ~MI_MACHINEDN_ALLOC;
}
pMachineInfo->MachineDN = BinlStrDup( *FilePath );
if ( pMachineInfo->MachineDN ) {
pMachineInfo->dwFlags |= MI_MACHINEDN | MI_MACHINEDN_ALLOC;
}
BinlPrint(( DEBUG_MISC, "MachineDN = %ws\n", pMachineInfo->MachineDN ));
ldap_value_free( FilePath );
} else {
BinlPrint((DEBUG_MISC,
"couldn't get distinguishedName for %ws\n",
Filter ));
}
FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"netbootInitialization" );
if ( FilePath ) {
if ( pMachineInfo->dwFlags & MI_SETUPPATH_ALLOC ) {
BinlFreeMemory( pMachineInfo->SetupPath );
pMachineInfo->dwFlags &= ~MI_SETUPPATH_ALLOC;
}
pMachineInfo->SetupPath = BinlStrDup( *FilePath );
if ( pMachineInfo->SetupPath ) {
pMachineInfo->dwFlags |= MI_SETUPPATH | MI_SETUPPATH_ALLOC;
BinlPrintDbg(( DEBUG_MISC, "SetupPath = %ws\n", pMachineInfo->SetupPath ));
}
ldap_value_free( FilePath );
}
FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"netbootMachineFilePath" );
if ( FilePath ) {
PWCHAR psz = wcschr( *FilePath, L'\\' );
if ( psz ) {
*psz = L'\0'; // terminate
}
if (pMachineInfo->dwFlags & MI_HOSTNAME_ALLOC) {
BinlFreeMemory( pMachineInfo->HostName );
pMachineInfo->dwFlags &= ~MI_HOSTNAME_ALLOC;
}
pMachineInfo->HostName = BinlStrDup( *FilePath );
if (pMachineInfo->HostName) {
BinlPrint(( DEBUG_MISC, "HostName = %ws\n", pMachineInfo->HostName ));
pMachineInfo->dwFlags |= MI_HOSTNAME | MI_HOSTNAME_ALLOC;
}
if ( psz ) {
*psz = L'\\'; // let's put it back to what it started as.
psz++;
if (pMachineInfo->dwFlags & MI_BOOTFILENAME_ALLOC) {
BinlFreeMemory( pMachineInfo->BootFileName );
pMachineInfo->dwFlags &= ~MI_BOOTFILENAME_ALLOC;
}
pMachineInfo->BootFileName = BinlStrDup( psz );
if ( pMachineInfo->BootFileName ) {
pMachineInfo->dwFlags |= MI_BOOTFILENAME | MI_BOOTFILENAME_ALLOC;
BinlPrintDbg(( DEBUG_MISC, "BootFileName = %ws\n", pMachineInfo->BootFileName ));
}
}
ldap_value_free( FilePath );
}
FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"netbootSIFFile" );
if ( FilePath ) {
PWSTR ForcedSifFilePath;
DWORD ForcedSifFileLength;
if (pMachineInfo->dwFlags & MI_SIFFILENAME_ALLOC) {
BinlFreeMemory( pMachineInfo->ForcedSifFileName );
pMachineInfo->dwFlags &= ~MI_SIFFILENAME_ALLOC;
}
ForcedSifFileLength = wcslen(*FilePath);
// d:\remoteinstall + '\' + NULL terminator
ForcedSifFileLength = ForcedSifFileLength + 1 + wcslen(IntelliMirrorPathW) + 1;
ForcedSifFileLength = ForcedSifFileLength * sizeof(WCHAR);
pMachineInfo->ForcedSifFileName = BinlAllocateMemory(ForcedSifFileLength);
if ( pMachineInfo->ForcedSifFileName ) {
if (_snwprintf( pMachineInfo->ForcedSifFileName,
ForcedSifFileLength/sizeof(WCHAR),
L"%ws\\%ws",
IntelliMirrorPathW,
*FilePath) >= 0) {
pMachineInfo->dwFlags |= MI_SIFFILENAME_ALLOC;
BinlPrintDbg(( DEBUG_MISC, "ForcedSifFileName = %ws\n", pMachineInfo->ForcedSifFileName ));
} else {
BinlPrintDbg(( DEBUG_MISC, "ForcedSifFileName _snwprintf failed\n" ));
BinlFreeMemory( pMachineInfo->ForcedSifFileName );
pMachineInfo->ForcedSifFileName = NULL;
}
}
ldap_value_free( FilePath );
}
if ( !(pMachineInfo->dwFlags & MI_HOSTNAME )
|| ( !pMachineInfo->HostName )
|| ( pMachineInfo->HostName[0] == L'\0') ) {
if ( pMachineInfo->dwFlags & MI_HOSTNAME_ALLOC ) {
BinlFreeMemory( pMachineInfo->HostName );
pMachineInfo->dwFlags &= ~MI_HOSTNAME_ALLOC;
pMachineInfo->HostName = NULL;
}
dwErr = BinlGenerateNewEntry( MI_HOSTNAME, SystemArchitecture, &pMachineInfo );
if ( dwErr != ERROR_SUCCESS ) {
goto e1;
}
}
if ( !(pMachineInfo->dwFlags & MI_BOOTFILENAME)
|| ( !pMachineInfo->BootFileName )
|| ( pMachineInfo->BootFileName[0] == L'\0') ) {
if (pMachineInfo->dwFlags & MI_BOOTFILENAME_ALLOC) {
BinlFreeMemory( pMachineInfo->BootFileName );
pMachineInfo->BootFileName = NULL;
pMachineInfo->dwFlags &= ~MI_BOOTFILENAME_ALLOC;
}
dwErr = BinlGenerateNewEntry( MI_BOOTFILENAME, SystemArchitecture, &pMachineInfo );
if ( dwErr != ERROR_SUCCESS ) {
goto e1;
}
}
FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"sAMAccountName" );
if ( FilePath ) {
if (pMachineInfo->dwFlags & MI_SAMNAME_ALLOC) {
BinlFreeMemory( pMachineInfo->SamName );
pMachineInfo->dwFlags &= ~MI_SAMNAME_ALLOC;
}
pMachineInfo->SamName = BinlStrDup( *FilePath );
if ( pMachineInfo->SamName ) {
pMachineInfo->dwFlags |= MI_SAMNAME | MI_SAMNAME_ALLOC;
BinlPrint(( DEBUG_MISC, "SamName = %ws\n", pMachineInfo->SamName ));
}
//
// For now, the pMachineInfo Name and SamName are the same values,
// therefore we won't look them up twice in the ldap message.
//
#if 0
ldap_value_free( FilePath );
}
FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"sAMAccountName" );
if ( FilePath ) {
#endif
if ( pMachineInfo->dwFlags & MI_NAME_ALLOC ) {
BinlFreeMemory( pMachineInfo->Name );
pMachineInfo->dwFlags &= ~MI_NAME_ALLOC;
}
pMachineInfo->Name = BinlStrDup( *FilePath );
if ( pMachineInfo->Name ) {
if( pMachineInfo->Name[ wcslen(pMachineInfo->Name) - 1 ] == L'$' ) {
pMachineInfo->Name[ wcslen(pMachineInfo->Name) - 1 ] = L'\0'; // remove '$'
}
pMachineInfo->dwFlags |= MI_NAME | MI_NAME_ALLOC;
BinlPrint(( DEBUG_MISC, "Name = %ws\n", pMachineInfo->Name ));
}
ldap_value_free( FilePath );
}
FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"dnsHostName" );
if ( FilePath ) {
BOOL fEndofString = FALSE;
PWCHAR psz = *FilePath;
// skip host name, we get that from the samName
while ( *psz && *psz!=L'.' ) {
psz++;
}
if ( !(*psz) ) {
fEndofString = TRUE;
}
*psz = L'\0'; // terminate
if ( fEndofString == FALSE ) {
psz++;
if (pMachineInfo->Domain) {
BinlFreeMemory( pMachineInfo->Domain );
}
pMachineInfo->Domain = BinlStrDup( psz );
if ( pMachineInfo->Domain )
{
pMachineInfo->dwFlags |= MI_DOMAIN;
BinlPrint(( DEBUG_MISC, "Domain = %ws\n", pMachineInfo->Domain ));
}
}
ldap_value_free(FilePath);
}
//
// track duplicates that we get back
//
// first we free all duplicates we have already allocated.
//
while (!IsListEmpty(&pMachineInfo->DNsWithSameGuid)) {
PLIST_ENTRY p = RemoveHeadList(&pMachineInfo->DNsWithSameGuid);
dupDN = CONTAINING_RECORD(p, DUP_GUID_DN, ListEntry);
BinlFreeMemory( dupDN );
}
while (--entryCount > 0) {
CurrentEntry = ldap_next_entry( LdapHandle, CurrentEntry );
if (CurrentEntry == NULL) {
break;
}
FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"dnsHostName" );
if (!FilePath) {
FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"sAMAccountName");
}
if ( FilePath ) {
ULONG dupLength, dupLength2;
BinlPrint(( DEBUG_OSC, "Found duplicate DN in %ws\n", *FilePath ));
FilePath2 = ldap_get_values( LdapHandle, CurrentEntry, L"distinguishedName");
dupLength = wcslen( *FilePath ) + 1;
if (FilePath2) {
dupLength2 = wcslen( *FilePath2 ) + 1;
} else {
dupLength2 = 1;
}
dupDN = BinlAllocateMemory( FIELD_OFFSET(DUP_GUID_DN, DuplicateName[0]) +
( (dupLength + dupLength2) * sizeof(WCHAR) ) );
if ( dupDN ) {
dupDN->DuplicateDNOffset = dupLength;
wcscpy( &dupDN->DuplicateName[0], *FilePath );
if (FilePath2) {
wcscpy( &dupDN->DuplicateName[dupLength], *FilePath2 );
} else {
dupDN->DuplicateName[dupLength] = L'\0';
}
//
// if the last character is a $, then slam in a NULL to end it.
//
if (( dupLength > 1 ) &&
( dupDN->DuplicateName[dupLength-2] == L'$' )) {
dupDN->DuplicateName[dupLength-2] = L'\0';
}
InsertTailList( &pMachineInfo->DNsWithSameGuid, &dupDN->ListEntry );
}
ldap_value_free( FilePath );
if (FilePath2) {
ldap_value_free( FilePath2 );
}
}
}
e1:
if (LdapMessage) {
ldap_msgfree( LdapMessage );
}
e0:
return dwErr;
}
DWORD
InitializeConnection(
BOOL Global,
PLDAP * LdapHandle,
PWCHAR ** Base )
/*++
Routine Description:
Initialize the ldap connection for operating on either the domain or the
global catalog.
Arguments:
Global - TRUE if GC should be used
LdapHandle - Returns the handle for further operations
OperationalAttributeLdapMessage - Returns message containing Base so that it can be freed later
Base - DN of where to start searches for computer objects.
Return Value:
ldap error
--*/
{
PLDAPMessage OperationalAttributeLdapMessage = NULL;
PWCHAR Attrs[2];
PLDAPMessage CurrentEntry;
PWCHAR *LdapValue;
DWORD LdapError = ERROR_SUCCESS;
PLDAP *LdapHandleCurrent;
PWCHAR ** LdapBaseCurrent;
ULONG temp;
TraceFunc( "InitializeConnection( )\n" );
// Use critical section to avoid two threads initialising the same parameters
EnterCriticalSection(&gcsDHCPBINL);
if ( !Global ) {
LdapHandleCurrent = &DCLdapHandle;
LdapBaseCurrent = &DCBase;
} else {
LdapHandleCurrent = &GCLdapHandle;
LdapBaseCurrent = &GCBase;
}
if ( !(*LdapHandleCurrent) ) {
if (Global) {
*LdapHandleCurrent = ldap_initW( BinlGlobalDefaultGC, LDAP_GC_PORT);
temp = DS_DIRECTORY_SERVICE_REQUIRED |
DS_IP_REQUIRED |
DS_GC_SERVER_REQUIRED;
} else {
*LdapHandleCurrent = ldap_initW( BinlGlobalDefaultDS, LDAP_PORT);
temp = DS_DIRECTORY_SERVICE_REQUIRED |
DS_IP_REQUIRED;
}
if (!*LdapHandleCurrent) {
BinlPrint(( DEBUG_ERRORS, "Failed to initialize LDAP connection.\n" ));
LdapError = LDAP_CONNECT_ERROR;
LogLdapError( (Global ? EVENT_WARNING_LDAP_INIT_ERROR_GC :
EVENT_WARNING_LDAP_INIT_ERROR_DC),
GetLastError(),
NULL
);
goto e0;
}
LdapError = ldap_set_option(*LdapHandleCurrent, LDAP_OPT_GETDSNAME_FLAGS, &temp );
if ( LdapError != LDAP_SUCCESS ) {
//
// something went wrong setting the option.
// do not continue, because we don't know
// what will happen with other settings
//
BinlPrint(( DEBUG_ERRORS, "Failed to set LDAP_OPT_GETDSNAME_FLAGS.\n"));
LogLdapError( EVENT_WARNING_LDAP_INIT_OPTIONS_ERROR,
LdapError,
*LdapHandleCurrent
);
goto e1;
}
if (Global == FALSE) {
temp = BinlLdapOptReferrals;
} else {
//
// At some future time, the GC is going to return referrals to
// authoritative DCs when the GC doesn't contain all the
// attributes. We'll enable referrals so that it "just works".
//
temp = (ULONG)((ULONG_PTR)LDAP_OPT_ON);
}
ldap_set_option(*LdapHandleCurrent, LDAP_OPT_REFERRALS, (void *) &temp );
if ( LdapError != LDAP_SUCCESS ) {
//
// something went wrong setting the option.
// do not continue, because we don't know
// what will happen with other settings
//
BinlPrint(( DEBUG_ERRORS, "Failed to set LDAP_OPT_REFERRALS.\n"));
LogLdapError( EVENT_WARNING_LDAP_INIT_OPTIONS_ERROR,
LdapError,
*LdapHandleCurrent
);
goto e1;
}
temp = LDAP_VERSION3;
ldap_set_option(*LdapHandleCurrent, LDAP_OPT_VERSION, &temp );
if ( LdapError != LDAP_SUCCESS ) {
//
// something went wrong setting the option.
// do not continue, because we don't know
// what will happen with other settings
//
BinlPrint(( DEBUG_ERRORS, "Failed to set LDAP_OPT_VERSION.\n"));
LogLdapError( EVENT_WARNING_LDAP_INIT_OPTIONS_ERROR,
LdapError,
*LdapHandleCurrent
);
goto e1;
}
LdapError = ldap_connect(*LdapHandleCurrent,0);
if (LdapError != LDAP_SUCCESS) {
LogLdapError( (Global ? EVENT_WARNING_LDAP_INIT_ERROR_GC :
EVENT_WARNING_LDAP_INIT_ERROR_DC),
LdapError,
*LdapHandleCurrent
);
BinlPrint(( DEBUG_ERRORS, "ldap_connect failed: %lx\n", LdapError ));
goto e1;
}
LdapError = ldap_bind_s(*LdapHandleCurrent, NULL, NULL, LDAP_AUTH_SSPI);
if (LdapError != LDAP_SUCCESS) {
BinlPrint(( DEBUG_ERRORS, "ldap_bind_s failed: %lx\n", LdapError ));
LogLdapError( EVENT_WARNING_LDAP_BIND_ERROR,
LdapError,
*LdapHandleCurrent
);
goto e1;
}
}
//
// Connected to Directory Service. Find out where in the DS we
// should start looking for the computer.
//
if ( !(*LdapBaseCurrent) )
{
DWORD count;
Attrs[0] = DefaultNamingContext;
Attrs[1] = NULL;
LdapError = ldap_search_ext_sW(*LdapHandleCurrent,
NULL, // base
LDAP_SCOPE_BASE,
L"objectClass=*",// filter
Attrs,
FALSE,
NULL,
NULL,
&BinlLdapSearchTimeout,
0,
&OperationalAttributeLdapMessage);
if ( LdapError != LDAP_SUCCESS ) {
BinlPrint(( DEBUG_ERRORS, "ldap_search_ext_s failed: %x\n", LdapError ));
HandleLdapFailure( LdapError,
EVENT_WARNING_LDAP_SEARCH_ERROR,
Global,
LdapHandleCurrent,
TRUE ); // we have lock
if (*LdapHandleCurrent == NULL) {
goto e1;
}
goto e2;
}
count = ldap_count_entries( *LdapHandleCurrent, OperationalAttributeLdapMessage );
if ( count == 0 ) {
BinlPrint(( DEBUG_ERRORS, "Failed to find the defaultNamingContext.\n" ));
LdapError = LDAP_NO_RESULTS_RETURNED;
LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
LdapError,
*LdapHandleCurrent
);
goto e2;
}
if ( count == -1 ) {
//
// get the failing case as well
//
BinlPrint(( DEBUG_ERRORS, "ldap_count_entries failed.\n" ));
LdapError = LdapGetLastError();
HandleLdapFailure( LdapError,
EVENT_WARNING_LDAP_COUNT_ENTRIES_ERROR,
Global,
LdapHandleCurrent,
TRUE ); // we have lock
if (*LdapHandleCurrent == NULL) {
goto e1;
}
goto e2;
}
//
// the DS should always only return us a single root DSE record.
// It would be completely broken if it returned more than one.
//
BinlAssert( count == 1 );
CurrentEntry = ldap_first_entry( *LdapHandleCurrent, OperationalAttributeLdapMessage );
LdapValue = ldap_get_values( *LdapHandleCurrent, CurrentEntry, Attrs[0] );
if (LdapValue == NULL) {
BinlPrint(( DEBUG_ERRORS, "Failed to find the defaultNamingContext.\n" ));
LdapError = LDAP_NO_RESULTS_RETURNED;
goto e2;
}
*LdapBaseCurrent = LdapValue;
}
e2:
if (OperationalAttributeLdapMessage) {
ldap_msgfree( OperationalAttributeLdapMessage );
}
e0:
if ( LdapHandle ) {
*LdapHandle = *LdapHandleCurrent;
}
if ( Base ) {
*Base = *LdapBaseCurrent;
}
LeaveCriticalSection(&gcsDHCPBINL);
return LdapError;
e1:
BinlPrint(( DEBUG_ERRORS, "Failed to connect to LDAP server.\n" ));
if (*LdapHandleCurrent != NULL) {
ldap_unbind(*LdapHandleCurrent);
*LdapHandleCurrent = NULL;
}
goto e0;
}
VOID
HandleLdapFailure(
DWORD LdapError,
DWORD EventId,
BOOL GlobalCatalog,
PLDAP *LdapHandle,
BOOL HaveLock
)
/*++
Routine Description:
This routine will recycle an ldap handle. Call this routine when one
of our global ldap handles becomes suspect because ldap calls start
failing.
Arguments:
LdapError - error code from ldap call that failed.
EventId - eventlog error that we should log on failure.
GlobalCatalog - set to TRUE if we were searching hte global catalog
and should therefore be cleaning up the GCLdapHandle.
LdapHandle - ldap handle pointer. it can be reset to null to indicate
that the handle was cleaned up and is no longer valid.
HaveLock - flag indicating if we are holding gcsDHCPBINL (required to free
a global structure like our handles).
Return Value:
None.
--*/
{
PLDAP *LdapHandleCurrent;
PWCHAR ** LdapBaseCurrent;
//
// it used to be that we'd only recycle the handle on certain failures, but
// it seems like we're likely to get any failure code on an invalid handle.
// therefore we always recycle the handle regardless of the error code.
//
if (LdapError == LDAP_SUCCESS) {
BinlAssert( FALSE );
}
if (!HaveLock) {
EnterCriticalSection(&gcsDHCPBINL);
}
LdapHandleCurrent = GlobalCatalog ? &GCLdapHandle : &DCLdapHandle;
LdapBaseCurrent = GlobalCatalog ? &GCBase : &DCBase;
if (EventId) {
LogLdapError( EventId,
LdapError,
(LdapHandle != NULL ? *LdapHandle : *LdapHandleCurrent)
);
}
if (LdapHandle) {
ASSERT( *LdapHandle == *LdapHandleCurrent );
*LdapHandle = NULL;
}
FreeConnection( LdapHandleCurrent, LdapBaseCurrent );
if (!HaveLock) {
LeaveCriticalSection(&gcsDHCPBINL);
}
return;
}
VOID
FreeConnection(
PLDAP * LdapHandle,
PWCHAR ** Base)
/*++
Routine Description:
Free the ldap connection for operating on either the domain or the
global catalog.
Arguments:
LdapHandle - The handle for further operations
Base - DN of where to start searches for computer objects to be freed.
Return Value:
None.
--*/
{
TraceFunc( "FreeConnection( )\n" );
if (*LdapHandle) {
ldap_unbind( *LdapHandle );
*LdapHandle = NULL;
}
if (*Base) {
ldap_value_free(*Base);
*Base = NULL;
}
}
VOID
FreeConnections
(
VOID
)
/*++
Routine Description:
Terminate any LDAP requests because we are stopping immediately. We
wait until all threads are stopped because the threads may have pointers
to the values we're going to free.
Arguments:
None.
Return Value:
None.
--*/
{
//
// clear out the cache, wait until all are marked as not being
// processed. We do this because the threads have pointers to DCBase,
// GCBase, etc and if we just blow them away, they may AV.
//
BinlCloseCache();
TraceFunc( "FreeConnections( )\n" );
FreeConnection( &DCLdapHandle, &DCBase);
FreeConnection( &GCLdapHandle, &GCBase);
}
DWORD
FindSCPForBinlServer(
PWCHAR * ResultPath,
PWCHAR * MachinePath,
BOOL GlobalSearch)
/*++
Routine Description:
Use the Directory Service to lookup the settings for this service.
Arguments:
GlobalSearch - TRUE if GC should be used
Return Value:
ERROR_SUCCESS or BINL_CANT_FIND_SERVER_MAO or ERROR_OUTOFMEMORY
--*/
{
DWORD Error = ERROR_SUCCESS;
PLDAP LdapHandle;
DWORD LdapError;
DWORD count;
ULONG ldapRetryLimit = 0;
PWCHAR * DsPath;
PLDAPMessage CurrentEntry;
PLDAPMessage LdapMessage = NULL;
PWCHAR ServerDN = NULL;
BOOL retryDN = TRUE;
// Paramters we want from the Computer Object
PWCHAR ComputerAttrs[2];
ComputerAttrs[0] = &L"netbootSCPBL";
ComputerAttrs[1] = NULL;
TraceFunc( "FindSCPForBinlServer( )\n" );
RetryGetDN:
//
// get ServerDN
//
// It should be something like this:
// ServerDN = "cn=server,cn=computers,dc=microsoft,dc=com"
//
EnterCriticalSection( &gcsParameters );
if (!BinlGlobalOurFQDNName) {
Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
}
ServerDN = StrDupW( BinlGlobalOurFQDNName );
LeaveCriticalSection( &gcsParameters );
if (!ServerDN ) {
if (Error == ERROR_SUCCESS) {
Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
}
goto e0;
}
Error = InitializeConnection(GlobalSearch, &LdapHandle, NULL);
if ( Error != ERROR_SUCCESS ) {
BinlPrintDbg(( DEBUG_ERRORS, "!!Error 0x%08x - Ldap Connection Failed.\n", Error ));
SetLastError( Error );
Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
goto e0;
}
RetrySearch:
LdapError = ldap_search_ext_sW(LdapHandle,
ServerDN,
LDAP_SCOPE_BASE,
L"objectClass=*",
ComputerAttrs,
FALSE,
NULL,
NULL,
&BinlLdapSearchTimeout,
0,
&LdapMessage);
//
// if the object isn't found, then something is amiss.. go grab the DN
// again.
//
if ((LdapError == LDAP_NO_SUCH_OBJECT) && retryDN) {
retryDN = FALSE;
// if we didn't find an entry or it was busy, retry
GetOurServerInfo();
BinlFreeMemory( ServerDN );
ServerDN = NULL;
goto RetryGetDN;
}
if (((LdapError == LDAP_BUSY) || (LdapError == LDAP_NO_SUCH_OBJECT)) &&
(++ldapRetryLimit < LDAP_BUSY_LIMIT)) {
Sleep( LDAP_BUSY_DELAY );
goto RetrySearch;
}
if (LdapError != LDAP_SUCCESS) {
//
// something is screwed up with our handle, get rid of it.
//
HandleLdapFailure(
LdapError,
EVENT_WARNING_LDAP_SEARCH_ERROR,
GlobalSearch,
&LdapHandle,
FALSE); // don't have lock
goto e1;
}
count = ldap_count_entries( LdapHandle, LdapMessage );
if (count == 0) {
if (LdapError == LDAP_SUCCESS) {
LdapError = LDAP_TIMELIMIT_EXCEEDED;
}
BinlPrintDbg(( DEBUG_ERRORS, "!!LdapError 0x%08x - LDAP search failed... will retry later.\n", LdapError ));
BinlReportEventW( EVENT_ERROR_LOCATING_SCP,
EVENTLOG_ERROR_TYPE,
0,
sizeof(LdapError),
NULL,
&LdapError
);
Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
goto e1;
}
BinlAssertMsg( count == 1, "Count should have been 1." );
if ( count != 1 ) {
BinlPrintDbg(( DEBUG_ERRORS, "!!Error - LDAP search returned more than one SCP record for us.\n" ));
BinlReportEventW( BINL_DUPLICATE_MAO_RECORD,
EVENTLOG_ERROR_TYPE,
0,
sizeof(count),
NULL,
&count
);
Error = ERROR_BINL_CANT_FIND_SERVER_MAO;
goto e1;
}
//
// Get the SCP
//
CurrentEntry = ldap_first_entry( LdapHandle, LdapMessage );
DsPath = ldap_get_values( LdapHandle, CurrentEntry, L"netbootSCPBL" );
if ( !DsPath ) {
BinlPrintDbg(( DEBUG_ERRORS, "!!Error - Could not get 'netbootSCPBL' from the server's MAO\n" ))
Error = ERROR_BINL_CANT_FIND_SERVER_MAO;
goto e1;
}
*ResultPath = (PWCHAR) BinlAllocateMemory( (wcslen(*DsPath) + 1) * sizeof(WCHAR) );
if ( *ResultPath == NULL ) {
BinlPrintDbg(( DEBUG_ERRORS, "!!Error - Out of memory.\n" ));
Error = ERROR_OUTOFMEMORY;
goto e2;
}
wcscpy( *ResultPath, *DsPath );
*MachinePath = ServerDN;
ServerDN = NULL; // prevent freeing
Error = ERROR_SUCCESS;
e2:
ldap_value_free(DsPath);
e1:
if (LdapMessage) {
ldap_msgfree( LdapMessage );
}
e0:
if ( ServerDN )
BinlFreeMemory( ServerDN );
return Error;
}
DWORD
UpdateSettingsUsingResults(
PLDAP LdapHandle,
PLDAPMessage LdapMessage,
LPWSTR ComputerAttrs[],
PDWORD NumberOfAttributesFound OPTIONAL
)
{
PLDAPMessage CurrentEntry;
DWORD LdapError = LDAP_SUCCESS;
DWORD count;
DWORD countFound = 0;
TraceFunc( "UpdateSettingsUsingResults( ... )\n" );
CurrentEntry = ldap_first_entry( LdapHandle, LdapMessage );
for ( count = 0; ComputerAttrs[count] != NULL; count++ ) {
PWCHAR * Attribute;
Attribute = ldap_get_values( LdapHandle, CurrentEntry, ComputerAttrs[count] );
if (Attribute == NULL) {
#if DBG
CHAR Temp[MAX_PATH];
if (BinlUnicodeToAnsi(ComputerAttrs[count],Temp,MAX_PATH)) {
BinlPrintDbg(( DEBUG_OPTIONS, "Did not find attribute '%s'... skipping\n", Temp ));
}
#endif
if (count != 1) { // NewMachineOU
continue; // skip and use default
}
} else {
//
// Increment the count of attributes found.
//
countFound++;
}
switch( count )
{
case 0: // NewMachineNamingPolicy
{
DWORD Length = wcslen( *Attribute ) + 1;
PWCHAR psz = (PWCHAR) BinlAllocateMemory( Length * sizeof(WCHAR) );
BinlAssert( _wcsicmp( ComputerAttrs[0], L"netbootNewMachineNamingPolicy" ) == 0 );
if ( psz )
{
wcscpy( psz, *Attribute );
EnterCriticalSection(&gcsParameters);
if ( NewMachineNamingPolicy != NULL )
{
BinlFreeMemory( NewMachineNamingPolicy );
}
NewMachineNamingPolicy = psz;
LeaveCriticalSection(&gcsParameters);
}
BinlPrint(( DEBUG_OPTIONS, "NewMachineNamingPolicy = '%ws'\n", NewMachineNamingPolicy ));
}
break;
case 1: // NewMachineOU
{
LPWSTR psz;
DWORD Length;
BOOL getServerInfo;
BinlAssert( _wcsicmp( ComputerAttrs[1], L"netbootNewMachineOU" ) == 0 );
if (Attribute == NULL || *Attribute == NULL) {
Length = 1;
} else {
Length = wcslen( *Attribute ) + 1;
}
psz = (LPWSTR) BinlAllocateMemory( Length * sizeof(WCHAR) );
if (psz == NULL) {
LdapError = LDAP_NO_MEMORY;
break;
}
if (Length == 1) {
*psz = L'\0';
} else {
wcscpy( psz, *Attribute );
}
EnterCriticalSection(&gcsParameters);
getServerInfo = (BOOL)( (BinlGlobalDefaultContainer == NULL) ||
(_wcsicmp(BinlGlobalDefaultContainer, psz) != 0) );
if ( BinlGlobalDefaultContainer != NULL )
{
BinlFreeMemory( BinlGlobalDefaultContainer );
}
BinlGlobalDefaultContainer = psz;
LeaveCriticalSection(&gcsParameters);
if ( getServerInfo ) {
ULONG Error = GetOurServerInfo();
if (Error != ERROR_SUCCESS) {
BinlPrintDbg(( DEBUG_ERRORS, "GetOurServerInfo returned 0x%x, we had a new default container.\n", Error ));
}
}
BinlPrint(( DEBUG_OPTIONS, "DefaultContainer = %ws\n", BinlGlobalDefaultContainer ));
}
break;
case 2: // MaxClients
{
CHAR Temp[10];
BinlAssert( _wcsicmp( ComputerAttrs[2], L"netbootMaxClients" ) == 0 );
if (!BinlUnicodeToAnsi(*Attribute,Temp,sizeof(Temp))) {
BinlMaxClients = atoi( Temp );
BinlPrint(( DEBUG_OPTIONS, "BinlMaxClients = %u\n", BinlMaxClients ));
}
}
break;
case 3: // CurrentClientCount
{
CHAR Temp[10];
BinlAssert( _wcsicmp( ComputerAttrs[3], L"netbootCurrentClientCount" ) == 0 );
if (!BinlUnicodeToAnsi(*Attribute,Temp,sizeof(Temp))) {
CurrentClientCount = atoi( Temp );
BinlPrint(( DEBUG_OPTIONS, "(Last) CurrentClientCount = %u\n", CurrentClientCount ));
}
}
break;
case 4: // AnswerRequest
BinlAssert( _wcsicmp ( ComputerAttrs[4], L"netbootAnswerRequests" ) == 0 );
if ( wcscmp( *Attribute, L"TRUE" ) == 0 )
{
AnswerRequests = TRUE;
}
else
{
AnswerRequests = FALSE;
}
BinlPrint(( DEBUG_OPTIONS, "AnswerRequests = %s\n", BOOLTOSTRING( AnswerRequests ) ));
break;
case 5: // AnswerOnlyValidClients
BinlAssert( _wcsicmp( ComputerAttrs[5], L"netbootAnswerOnlyValidClients" ) == 0 );
if ( wcscmp( *Attribute, L"TRUE" ) == 0 ) {
AnswerOnlyValidClients = TRUE;
} else {
AnswerOnlyValidClients = FALSE;
}
BinlPrint(( DEBUG_OPTIONS, "AnswerOnlyValidClients = %s\n", BOOLTOSTRING( AnswerOnlyValidClients ) ));
break;
case 6: // AllowNewClients
BinlAssert( _wcsicmp( ComputerAttrs[6], L"netbootAllowNewClients" ) == 0 );
if ( wcscmp( *Attribute, L"TRUE" ) == 0 )
{
AllowNewClients = TRUE;
}
else
{
AllowNewClients = FALSE;
}
BinlPrint(( DEBUG_OPTIONS, "AllowNewClients = %s\n", BOOLTOSTRING( AllowNewClients ) ));
break;
case 7: // LimitClients
BinlAssert( _wcsicmp( ComputerAttrs[7], L"netbootLimitClients" ) == 0 );
if ( wcscmp( *Attribute, L"TRUE" ) == 0 )
{
LimitClients = TRUE;
}
else
{
LimitClients = FALSE;
}
BinlPrint(( DEBUG_OPTIONS, "LimitClients = %s\n", BOOLTOSTRING( LimitClients ) ));
break;
case 8: // IntellimirrorOSes
case 9: // Tools
case 10: // LocalInstallOSes
BinlAssert( _wcsicmp( ComputerAttrs[8], L"netbootIntellimirrorOSes" ) == 0 );
BinlAssert( _wcsicmp( ComputerAttrs[9], L"netbootTools" ) == 0 );
BinlAssert( _wcsicmp( ComputerAttrs[10], L"netbootLocalInstallOSes" ) == 0 );
//
// TODO: Tie these in with OS Chooser - this is still TBD.
//
break;
default:
// Somethings wrong
BinlAssert( 0 );
}
if (Attribute != NULL) {
ldap_value_free(Attribute);
}
}
if ( ARGUMENT_PRESENT(NumberOfAttributesFound) ) {
*NumberOfAttributesFound = countFound;
}
return LdapError;
}
DWORD
GetBinlServerParameters(
BOOL GlobalSearch)
/*++
Routine Description:
Use the Directory Service to lookup the settings for this service.
Arguments:
GlobalSearch - TRUE if GC should be used
Return Value:
ERROR_SUCCESS or BINL_CANT_FIND_SERVER_MAO
--*/
{
DWORD Error;
PLDAP LdapHandle;
DWORD LdapError;
DWORD count;
ULONG ldapRetryLimit = 0;
PLDAPMessage LdapMessage = NULL;
// Paramters we want from the IntelliMirror-SCP
// NOTE: These must be the same ordinals as those used in
// UpdateSettingsUsingResults( ).
PWCHAR ComputerAttrs[12];
ComputerAttrs[0] = &L"netbootNewMachineNamingPolicy";
ComputerAttrs[1] = &L"netbootNewMachineOU";
ComputerAttrs[2] = &L"netbootMaxClients";
ComputerAttrs[3] = &L"netbootCurrentClientCount";
ComputerAttrs[4] = &L"netbootAnswerRequests";
ComputerAttrs[5] = &L"netbootAnswerOnlyValidClients";
ComputerAttrs[6] = &L"netbootAllowNewClients";
ComputerAttrs[7] = &L"netbootLimitClients";
ComputerAttrs[8] = &L"netbootIntellimirrorOSes";
ComputerAttrs[9] = &L"netbootTools";
ComputerAttrs[10] = &L"netbootLocalInstallOSes";
ComputerAttrs[11] = NULL;
TraceFunc( "GetBinlServerParameters( )\n" );
Error = FindSCPForBinlServer( &BinlGlobalSCPPath, &BinlGlobalServerDN, GlobalSearch );
if ( Error != ERROR_SUCCESS ) {
BinlPrint(( DEBUG_ERRORS, "!!Error 0x%08x - SCP not found. Default settings being used.\n", Error ));
goto e0;
}
BinlPrint(( DEBUG_OPTIONS, "ServerDN = '%ws'\n", BinlGlobalServerDN ));
BinlPrint(( DEBUG_OPTIONS, "SCPDN = '%ws'\n", BinlGlobalSCPPath ));
RetryConnection:
Error = InitializeConnection( GlobalSearch, &LdapHandle, NULL );
if ( Error != ERROR_SUCCESS ) {
SetLastError( Error );
Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
goto e0;
}
Retry:
LdapError = ldap_search_ext_sW(LdapHandle,
BinlGlobalSCPPath,
LDAP_SCOPE_BASE,
L"objectClass=*",
ComputerAttrs,
FALSE,
NULL,
NULL,
NULL,
0,
&LdapMessage);
if ((LdapError == LDAP_BUSY) && (++ldapRetryLimit < LDAP_BUSY_LIMIT)) {
Sleep( LDAP_BUSY_DELAY );
goto Retry;
}
if (LdapError != LDAP_SUCCESS) {
HandleLdapFailure( LdapError,
EVENT_WARNING_LDAP_SEARCH_ERROR,
GlobalSearch,
&LdapHandle,
FALSE ); // don't have lock
if (LdapHandle == NULL) {
if (++ldapRetryLimit < LDAP_SERVER_DOWN_LIMIT) {
goto RetryConnection;
}
goto e0;
}
goto e1;
}
count = ldap_count_entries( LdapHandle, LdapMessage );
if (count == 0) {
if (LdapError == LDAP_SUCCESS) {
LdapError = LDAP_TIMELIMIT_EXCEEDED;
}
BinlPrintDbg(( DEBUG_ERRORS, "!!LdapError 0x%08x - Failed to retrieve parameters... will retry later.\n", LdapError ));
BinlReportEventW( EVENT_ERROR_LOCATING_SCP,
EVENTLOG_ERROR_TYPE,
0,
sizeof(LdapError),
NULL,
&LdapError
);
Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
goto e1;
}
BinlAssertMsg( count == 1, "Count should have been one. Is the SCP missing?" );
// We did a base level search, we better only have gotten one record back.
BinlAssert( count == 1 );
// Retrieve the results into the settings
LdapError = UpdateSettingsUsingResults( LdapHandle, LdapMessage, ComputerAttrs, &count );
if ( LdapError == LDAP_SUCCESS )
{
BinlReportEventW( count != 0 ? EVENT_SCP_READ_SUCCESSFULLY :
EVENT_SCP_READ_SUCCESSFULLY_EMPTY,
count != 0 ? EVENTLOG_INFORMATION_TYPE :
EVENTLOG_WARNING_TYPE,
0,
0,
NULL,
NULL
);
}
e1:
if (LdapMessage) {
ldap_msgfree( LdapMessage );
}
e0:
return Error;
}
VOID
BinlLogDuplicateDsRecords (
LPGUID Guid,
LDAP *LdapHandle,
LDAPMessage *LdapMessage,
LDAPMessage *CurrentEntry
)
//
// Log an error that we've received duplicate records for a client when
// we looked them up by GUID.
//
// We log the DNs so that the administrator can look them up.
//
{
LPWSTR strings[4];
LPWSTR dn1;
LPWSTR dn2;
ULONG strCount = 0; // up to two strings to log
PLDAPMessage nextEntry = ldap_next_entry( LdapHandle, LdapMessage );
LPWSTR GuidString;
if (SUCCEEDED(StringFromIID( (REFIID)Guid, &GuidString ))) {
strCount += 1;
}
dn1 = ldap_get_dnW( LdapHandle, CurrentEntry );
if (nextEntry != NULL) {
dn2 = ldap_get_dnW( LdapHandle, nextEntry );
} else {
dn2 = NULL;
}
if (dn2 != NULL) {
if (dn1 == NULL) {
dn1 = dn2;
dn2 = NULL;
} else {
strCount += 1;
}
}
if (dn1 != NULL) {
strCount += 1;
}
BinlPrint(( DEBUG_ERRORS, "Warning - BINL received multiple records for a single GUID.\n" ));
strings[0] = GuidString;
strings[1] = dn1;
strings[2] = dn2;
strings[3] = NULL;
BinlReportEventW( BINL_DUPLICATE_DS_RECORD,
EVENTLOG_WARNING_TYPE,
strCount,
0,
strings,
NULL
);
ldap_memfree( dn1 ); // it's ok to call ldap_memfree with null
ldap_memfree( dn2 );
CoTaskMemFree( GuidString );
}
#ifndef DSCRACKNAMES_DNS
DWORD
BinlDNStoFQDN(
PWCHAR pMachineDNS,
PWCHAR * ppMachineDN )
{
DWORD Error;
DWORD LdapError;
WCHAR FilterTemplate[] = L"dnsHostName=%ws";
PWCHAR Filter = NULL;
PWCHAR ComputerAttrs[2];
PLDAPMessage CurrentEntry;
PLDAPMessage LdapMessage = NULL;
LDAP *LdapHandle;
PWCHAR * Base;
PWCHAR * MachineDN;
DWORD count;
DWORD uSize;
ULONG ldapRetryLimit = 0;
TraceFunc( "BinlDNStoFQDN( )\n" );
BinlAssert( ppMachineDN );
BinlAssert( pMachineDNS );
ComputerAttrs[0] = &L"distinguishedName";
ComputerAttrs[1] = NULL;
// Build the filter to find the Computer object
uSize = sizeof(FilterTemplate) // include NULL terminater
+ (wcslen( pMachineDNS ) * sizeof(WCHAR));
Filter = (LPWSTR) BinlAllocateMemory( uSize );
if ( !Filter ) {
Error = E_OUTOFMEMORY;
goto e0;
}
wsprintf( Filter, FilterTemplate, pMachineDNS );
BinlPrintDbg(( DEBUG_MISC, "Searching for %ws...\n", Filter ));
RetryConnection:
Error = InitializeConnection( FALSE, &LdapHandle, &Base );
if ( Error != ERROR_SUCCESS ) {
SetLastError( Error );
Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
goto e0;
}
Retry:
LdapError = ldap_search_ext_sW( LdapHandle,
*Base,
LDAP_SCOPE_SUBTREE,
Filter,
ComputerAttrs,
FALSE,
NULL,
NULL,
NULL,
0,
&LdapMessage);
switch (LdapError)
{
case LDAP_SUCCESS:
break;
case LDAP_BUSY:
if (++ldapRetryLimit < LDAP_BUSY_LIMIT) {
Sleep( LDAP_BUSY_DELAY );
goto Retry;
}
// lack of break is on purpose.
default:
BinlPrintDbg(( DEBUG_ERRORS, "!!LdapError 0x%08x - Search failed in DNStoFQDN.\n", LdapError ));
HandleLdapFailure( LdapError,
EVENT_WARNING_LDAP_SEARCH_ERROR,
FALSE,
&LdapHandle,
FALSE ); // don't have lock
if (LdapHandle == NULL) {
if (++ldapRetryLimit < LDAP_SERVER_DOWN_LIMIT) {
goto RetryConnection;
}
}
goto e1;
}
// Did we get a Computer Object?
count = ldap_count_entries( LdapHandle, LdapMessage );
if ( count == 0 ) {
Error = ERROR_BINL_UNABLE_TO_CONVERT;
goto e1; // nope
}
// if we get more than more entry back, we will use only the
// first one.
CurrentEntry = ldap_first_entry( LdapHandle, LdapMessage );
MachineDN = ldap_get_values( LdapHandle, CurrentEntry, ComputerAttrs[0] );
if ( !MachineDN ) {
Error = ERROR_BINL_UNABLE_TO_CONVERT;
goto e1;
}
*ppMachineDN = BinlStrDup( *MachineDN );
Error = ERROR_SUCCESS;
ldap_value_free( MachineDN );
e1:
if (LdapMessage) {
ldap_msgfree( LdapMessage );
}
e0:
return Error;
}
#endif // DSCRACKNAMES_DNS
// message.c eof