Source code of Windows XP (NT5)
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.

3854 lines
112 KiB

  1. /*++
  2. Copyright (c) 1997-1998 Microsoft Corporation
  3. Module Name:
  4. message.c
  5. Abstract:
  6. This module contains the code to process a BINL request message
  7. for the BINL server.
  8. Author:
  9. Colin Watson (colinw) 2-May-1997
  10. Environment:
  11. User Mode - Win32
  12. Revision History:
  13. --*/
  14. #include "binl.h"
  15. #pragma hdrstop
  16. #if DBG
  17. DWORD BinlRepeatSleep;
  18. #endif
  19. const WCHAR IntelOSChooser[] = L"OSChooser\\i386\\startrom.com";
  20. const WCHAR IA64OSChooser[] = L"OSChooser\\ia64\\oschoice.efi";
  21. // Connection information to a DC in our domain
  22. PLDAP DCLdapHandle = NULL;
  23. PWCHAR * DCBase = NULL;
  24. // Connection information to the Global Catalog for our enterprise
  25. PLDAP GCLdapHandle = NULL;
  26. PWCHAR * GCBase = NULL;
  27. DWORD
  28. GetGuidFromPacket(
  29. LPDHCP_MESSAGE DhcpReceiveMessage,
  30. LPDHCP_SERVER_OPTIONS DhcpOptions,
  31. OUT PUCHAR Guid,
  32. OUT PDWORD GuidLength OPTIONAL,
  33. OUT PMACHINE_INFO *MachineInfo
  34. );
  35. LPOPTION
  36. AppendClientRequestedParameters(
  37. DHCP_IP_ADDRESS IpAddress,
  38. DHCP_IP_ADDRESS SubnetMask,
  39. LPBYTE RequestedList,
  40. DWORD ListLength,
  41. LPOPTION Option,
  42. LPBYTE OptionEnd,
  43. CHAR *ClassIdentifier,
  44. DWORD ClassIdentifierLength,
  45. BOOL fSwitchedSubnet
  46. );
  47. DWORD
  48. RecognizeClient(
  49. PUCHAR pGuid,
  50. PMACHINE_INFO * pMachineInfo,
  51. DWORD dwRequestedInfo,
  52. ULONG SecondsSinceBoot,
  53. USHORT SystemArchitecture
  54. );
  55. DWORD
  56. GetBootParametersExt(
  57. PMACHINE_INFO pMachineInfo,
  58. DWORD dwRequestedInfo,
  59. USHORT SystemArchitecture,
  60. BOOL fGlobal);
  61. VOID
  62. HandleLdapFailure(
  63. DWORD LdapError,
  64. DWORD EventId,
  65. BOOL GlobalCatalog,
  66. PLDAP *LdapHandle,
  67. BOOL HaveLock
  68. );
  69. VOID
  70. FreeConnection(
  71. PLDAP * LdapHandle,
  72. PWCHAR ** Base
  73. );
  74. DWORD
  75. ProcessMessage(
  76. LPBINL_REQUEST_CONTEXT RequestContext
  77. )
  78. /*++
  79. Routine Description:
  80. This function dispatches the processing of a received BINL message.
  81. The handler functions will create the response message if necessary.
  82. Arguments:
  83. RequestContext - A pointer to the BinlRequestContext block for
  84. this request.
  85. Return Value:
  86. Windows Error.
  87. --*/
  88. {
  89. DWORD Error;
  90. BOOL fSendResponse,
  91. fSubnetsListEmpty,
  92. fReadyToTerminate,
  93. fAllThreadsBusy;
  94. DHCP_SERVER_OPTIONS dhcpOptions;
  95. LPDHCP_MESSAGE binlReceiveMessage;
  96. TraceFunc("ProcessMessage( )\n" );
  97. //
  98. // Simply ignore messages when the service is paused.
  99. //
  100. if( BinlGlobalServiceStatus.dwCurrentState == SERVICE_PAUSED )
  101. {
  102. Error = ERROR_BINL_SERVICE_PAUSED;
  103. goto t_done;
  104. }
  105. binlReceiveMessage = (LPDHCP_MESSAGE)RequestContext->ReceiveBuffer;
  106. //
  107. // If it is an OSChooser message, then process that separately
  108. // since they don't conform to the DHCP layout. This will send
  109. // any messages that it needs to.
  110. //
  111. if (binlReceiveMessage->Operation == OSC_REQUEST)
  112. {
  113. Error = OscProcessMessage(RequestContext);
  114. goto t_done;
  115. }
  116. RtlZeroMemory( &dhcpOptions, sizeof( dhcpOptions ) );
  117. //BinlDumpMessage(DEBUG_MESSAGE, binlReceiveMessage);
  118. Error = ExtractOptions(
  119. binlReceiveMessage,
  120. &dhcpOptions,
  121. RequestContext->ReceiveMessageSize );
  122. if( Error != ERROR_SUCCESS ) {
  123. goto t_done;
  124. }
  125. if (!dhcpOptions.MessageType) {
  126. goto t_done; // BOOTP request
  127. }
  128. #if 0
  129. if (dhcpOptions.SystemArchitecture
  130. != DHCP_OPTION_CLIENT_ARCHITECTURE_X86) {
  131. BinlPrintDbg((
  132. DEBUG_OPTIONS,
  133. "ProcessMessage: Client ignored - unsupported architecture type %d \n",
  134. dhcpOptions.SystemArchitecture ) );
  135. goto t_done;
  136. }
  137. #endif
  138. if ( ( !AnswerRequests ) &&
  139. ( RequestContext->ActiveEndpoint->Port == DHCP_SERVR_PORT )) {
  140. //
  141. // this is not the 4011 port, therefore it must be the DHCP port.
  142. // We're configured to not answer requests on this port right now
  143. // therefore we'll toss this packet.
  144. //
  145. BinlPrint((DEBUG_OPTIONS, "Client ignored - Not answering requests (AnswerRequests == FALSE)\n" ));
  146. goto t_done;
  147. }
  148. if (BinlGlobalAuthorized == FALSE) {
  149. BinlPrint((DEBUG_ROGUE, "BINL has not passed rogue detection. Ignoring packet.\n" ));
  150. //
  151. // We'll possibly log an event here since we don't log an event
  152. // at startup saying what our rogue state is.
  153. //
  154. LogCurrentRogueState( TRUE );
  155. goto t_done;
  156. }
  157. //
  158. // Dispatch based on Message Type
  159. //
  160. RequestContext->MessageType = *dhcpOptions.MessageType;
  161. switch( *dhcpOptions.MessageType ) {
  162. case DHCP_DISCOVER_MESSAGE:
  163. Error = ProcessBinlDiscover( RequestContext, &dhcpOptions );
  164. fSendResponse = TRUE;
  165. break;
  166. case DHCP_INFORM_MESSAGE:
  167. Error = ProcessBinlInform( RequestContext, &dhcpOptions );
  168. fSendResponse = TRUE;
  169. break;
  170. case DHCP_REQUEST_MESSAGE:
  171. Error = ProcessBinlRequest( RequestContext, &dhcpOptions );
  172. fSendResponse = TRUE;
  173. break;
  174. default:
  175. BinlPrintDbg(( DEBUG_STOC,
  176. "Received a invalid message type, %ld.\n",
  177. *dhcpOptions.MessageType ));
  178. Error = ERROR_BINL_INVALID_BINL_MESSAGE;
  179. break;
  180. }
  181. if ( ERROR_SUCCESS == Error && fSendResponse )
  182. {
  183. /*
  184. BinlDumpMessage(
  185. DEBUG_MESSAGE,
  186. (LPDHCP_MESSAGE)RequestContext->SendBuffer
  187. );
  188. */
  189. BinlSendMessage( RequestContext );
  190. }
  191. t_done:
  192. //
  193. // delete the context structure for this thread
  194. //
  195. BinlFreeMemory( RequestContext->ReceiveBuffer );
  196. BinlFreeMemory( RequestContext->SendBuffer );
  197. BinlFreeMemory( RequestContext );
  198. EnterCriticalSection( &g_ProcessMessageCritSect );
  199. //
  200. // Check to see if all worker threads were busy
  201. //
  202. fAllThreadsBusy = ( g_cProcessMessageThreads ==
  203. g_cMaxProcessingThreads );
  204. --g_cProcessMessageThreads;
  205. //
  206. // Check to see if this is the last worker thread
  207. //
  208. fReadyToTerminate = !g_cProcessMessageThreads;
  209. LeaveCriticalSection( &g_ProcessMessageCritSect );
  210. //
  211. // If all the worker threads were busy, then BinlProcessingLoop
  212. // is waiting for a thread to complete. Set BinlGlobalRecvEvent
  213. // so BinlProcessingLoop can continue.
  214. //
  215. if ( fAllThreadsBusy )
  216. {
  217. BinlPrintDbg( ( DEBUG_STOC,
  218. "ProcessMessage: Alerting BinlProcessingLoop\n" )
  219. );
  220. SetEvent( BinlGlobalRecvEvent );
  221. }
  222. if ( fReadyToTerminate &&
  223. WaitForSingleObject( BinlGlobalProcessTerminationEvent,
  224. 0 ) == WAIT_OBJECT_0 )
  225. {
  226. //
  227. // there are no other ProcessMessage threads running, and
  228. // the service is waiting to shutdown.
  229. //
  230. BinlPrintDbg( (DEBUG_MISC,
  231. "ProcessMessage: shutdown complete.\n" )
  232. );
  233. BinlAssert( g_hevtProcessMessageComplete );
  234. SetEvent( g_hevtProcessMessageComplete );
  235. }
  236. //
  237. // thread exit
  238. //
  239. BinlPrintDbg( ( DEBUG_STOC,
  240. "ProcessMessage exited\n" )
  241. );
  242. return Error;
  243. }
  244. DWORD
  245. GetGuidFromPacket(
  246. LPDHCP_MESSAGE DhcpReceiveMessage,
  247. LPDHCP_SERVER_OPTIONS DhcpOptions,
  248. OUT PUCHAR Guid,
  249. OUT PDWORD GuidLength OPTIONAL,
  250. OUT PMACHINE_INFO *MachineInfo
  251. )
  252. {
  253. DWORD gLength = BINL_GUID_LENGTH;
  254. const LONG AllFs[] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF };
  255. const LONG AllZeros[] = { 0x0, 0x0, 0x0, 0x0 };
  256. DWORD err;
  257. ULONG SecondsSinceBoot;
  258. TraceFunc("GetGuidFromPacket( )\n" );
  259. BinlAssert(sizeof(AllZeros) == BINL_GUID_LENGTH );
  260. BinlAssert(sizeof(AllFs) == BINL_GUID_LENGTH);
  261. if (DhcpOptions->GuidLength == 0) {
  262. DWORD bytesToCopy;
  263. useNicAddress:
  264. memset(Guid, 0x0, BINL_GUID_LENGTH);
  265. if (DhcpReceiveMessage->HardwareAddressLength > BINL_GUID_LENGTH) {
  266. bytesToCopy = BINL_GUID_LENGTH;
  267. } else {
  268. bytesToCopy = DhcpReceiveMessage->HardwareAddressLength;
  269. }
  270. memcpy(Guid + BINL_GUID_LENGTH - bytesToCopy,
  271. DhcpReceiveMessage->HardwareAddress,
  272. bytesToCopy
  273. );
  274. } else {
  275. if (DhcpOptions->GuidLength > BINL_GUID_LENGTH) {
  276. memcpy(Guid, DhcpOptions->Guid + DhcpOptions->GuidLength - BINL_GUID_LENGTH, BINL_GUID_LENGTH);
  277. } else {
  278. gLength = DhcpOptions->GuidLength;
  279. memcpy(Guid, DhcpOptions->Guid, gLength);
  280. }
  281. if (!memcmp(Guid, (PUCHAR)AllFs, BINL_GUID_LENGTH) ||
  282. !memcmp(Guid, (PUCHAR)AllZeros, BINL_GUID_LENGTH)) {
  283. //
  284. // if they specified all 00s or all FFs, use the NIC address.
  285. //
  286. goto useNicAddress;
  287. }
  288. }
  289. if (GuidLength) {
  290. *GuidLength = gLength;
  291. }
  292. //
  293. // we return STATUS_SUCCESS if we can handle this client.
  294. //
  295. // If a cache entry is found here, then it will be marked as InProgress as
  296. // a side effect of finding it. We need to call BinlDoneWithCacheEntry
  297. // when we're done with the entry.
  298. //
  299. // SecondsSinceBoot may have been sent on the network in network order.
  300. // To correct this we assume the lower of the two bytes is the high byte.
  301. // So if the high byte is more than the low one, we flip them.
  302. //
  303. SecondsSinceBoot = DhcpReceiveMessage->SecondsSinceBoot;
  304. if ((SecondsSinceBoot >> 8) > (SecondsSinceBoot % 256)) {
  305. SecondsSinceBoot = (SecondsSinceBoot >> 8) +
  306. ((SecondsSinceBoot % 256) << 8);
  307. }
  308. err = RecognizeClient( Guid,
  309. MachineInfo,
  310. MI_HOSTNAME | MI_BOOTFILENAME,
  311. SecondsSinceBoot,
  312. DhcpOptions->SystemArchitecture );
  313. if ( err == ERROR_BINL_INVALID_GUID ) {
  314. PWCHAR pwch;
  315. //
  316. // Log an event with the hardware address of the offending client
  317. //
  318. pwch = (PWCHAR)BinlAllocateMemory((10 * DhcpReceiveMessage->HardwareAddressLength + 1) * sizeof(WCHAR));
  319. if (pwch != NULL) {
  320. INT i;
  321. *pwch = UNICODE_NULL;
  322. for (i=0 ; i < DhcpReceiveMessage->HardwareAddressLength; i++) {
  323. WCHAR Buffer[5];
  324. swprintf(Buffer, L" 0x%2x", (ULONG)(DhcpReceiveMessage->HardwareAddress[i]));
  325. wcscat(pwch, Buffer);
  326. }
  327. BinlReportEventW(EVENT_SERVER_CLIENT_WITHOUT_GUID,
  328. EVENTLOG_INFORMATION_TYPE,
  329. 1,
  330. 0,
  331. &pwch,
  332. NULL
  333. );
  334. BinlFreeMemory( pwch );
  335. }
  336. }
  337. return err;
  338. }
  339. DWORD
  340. ProcessBinlDiscoverInDhcp(
  341. LPDHCP_MESSAGE DhcpReceiveMessage,
  342. LPDHCP_SERVER_OPTIONS DhcpOptions
  343. )
  344. {
  345. DWORD Error;
  346. UCHAR Guid[BINL_GUID_LENGTH];
  347. PMACHINE_INFO machineInfo = NULL;
  348. TraceFunc("ProcessBinlDiscoverInDhcp( )\n" );
  349. if ( !AnswerRequests ) {
  350. BinlPrint((DEBUG_OPTIONS, "Client ignored - Not answering requests (AnswerRequests == FALSE)\n" ));
  351. return ERROR_BINL_INVALID_BINL_CLIENT;
  352. }
  353. if (BinlGlobalAuthorized == FALSE) {
  354. BinlPrint((DEBUG_ROGUE, "BINL has not passed rogue detection. Ignoring packet.\n" ));
  355. //
  356. // We'll possibly log an event here since we don't log an event
  357. // at startup saying what our rogue state is.
  358. //
  359. LogCurrentRogueState( TRUE );
  360. return ERROR_BINL_INVALID_BINL_CLIENT;
  361. }
  362. //
  363. // If a cacheEntry is found here, then it will be marked as InProgress as
  364. // a side effect of finding it. We need to call BinlDoneWithCacheEntry
  365. // when we're done with the entry.
  366. //
  367. Error = GetGuidFromPacket( DhcpReceiveMessage,
  368. DhcpOptions,
  369. Guid,
  370. NULL,
  371. &machineInfo
  372. );
  373. if (machineInfo != NULL) {
  374. BinlDoneWithCacheEntry( machineInfo, FALSE );
  375. }
  376. if( Error != ERROR_SUCCESS ) {
  377. BinlPrint(( DEBUG_STOC, "BinlDiscover failed with Dhcp server, 0x%x\n", Error ));
  378. }
  379. return( Error );
  380. }
  381. DWORD
  382. ProcessBinlDiscover(
  383. LPBINL_REQUEST_CONTEXT RequestContext,
  384. LPDHCP_SERVER_OPTIONS DhcpOptions
  385. )
  386. /*++
  387. Routine Description:
  388. This function will create the response message if necessary.
  389. Arguments:
  390. RequestContext - A pointer to the BinlRequestContext block for
  391. this request.
  392. dhcpOptions - Interesting options extracted from the request.
  393. Return Value:
  394. Windows Error.
  395. --*/
  396. {
  397. DWORD Error;
  398. LPDHCP_MESSAGE dhcpReceiveMessage;
  399. LPDHCP_MESSAGE dhcpSendMessage;
  400. BYTE messageType;
  401. LPOPTION Option;
  402. LPBYTE OptionEnd;
  403. PMACHINE_INFO pMachineInfo = NULL;
  404. UCHAR Guid[ BINL_GUID_LENGTH ];
  405. DHCP_IP_ADDRESS ipaddr;
  406. TraceFunc("ProcessBinlDiscover( )\n" );
  407. dhcpReceiveMessage = (LPDHCP_MESSAGE) RequestContext->ReceiveBuffer;
  408. //
  409. // If the client specified a server identifier option, we should
  410. // drop this packet unless the identified server is this one.
  411. //
  412. ipaddr = BinlGetMyNetworkAddress( RequestContext );
  413. if ( ipaddr == 0 ) {
  414. Error = ERROR_BINL_INVALID_BINL_CLIENT;
  415. goto Cleanup;
  416. }
  417. if ( DhcpOptions->Server != NULL ) {
  418. if ( *DhcpOptions->Server != ipaddr ) {
  419. Error = ERROR_BINL_INVALID_BINL_CLIENT;
  420. goto Cleanup;
  421. }
  422. }
  423. //
  424. // If a cacheEntry is found here, then it will be marked as InProgress as
  425. // a side effect of finding it. We need to call BinlDoneWithCacheEntry
  426. // when we're done with the entry.
  427. //
  428. Error = GetGuidFromPacket( dhcpReceiveMessage,
  429. DhcpOptions,
  430. Guid,
  431. NULL,
  432. &pMachineInfo
  433. );
  434. if (Error != ERROR_SUCCESS) {
  435. goto Cleanup;
  436. }
  437. //
  438. // Generate and send a reply.
  439. //
  440. dhcpReceiveMessage->BootFileName[ BOOT_FILE_SIZE - 1 ] = '\0';
  441. dhcpSendMessage = (LPDHCP_MESSAGE) RequestContext->SendBuffer;
  442. RtlZeroMemory( RequestContext->SendBuffer, DHCP_SEND_MESSAGE_SIZE );
  443. dhcpSendMessage->Operation = BOOT_REPLY;
  444. dhcpSendMessage->TransactionID = dhcpReceiveMessage->TransactionID;
  445. dhcpSendMessage->ClientIpAddress = dhcpReceiveMessage->ClientIpAddress;
  446. dhcpSendMessage->YourIpAddress = dhcpReceiveMessage->YourIpAddress;
  447. if (pMachineInfo != NULL && pMachineInfo->HostAddress != 0) {
  448. dhcpSendMessage->BootstrapServerAddress = pMachineInfo->HostAddress;
  449. } else {
  450. dhcpSendMessage->BootstrapServerAddress = ipaddr;
  451. }
  452. dhcpSendMessage->RelayAgentIpAddress = dhcpReceiveMessage->RelayAgentIpAddress;
  453. dhcpSendMessage->Reserved = dhcpReceiveMessage->Reserved;
  454. dhcpSendMessage->HardwareAddressType = dhcpReceiveMessage->HardwareAddressType;
  455. dhcpSendMessage->HardwareAddressLength = dhcpReceiveMessage->HardwareAddressLength;
  456. RtlCopyMemory(dhcpSendMessage->HardwareAddress,
  457. dhcpReceiveMessage->HardwareAddress,
  458. dhcpReceiveMessage->HardwareAddressLength );
  459. Option = &dhcpSendMessage->Option;
  460. OptionEnd = (LPBYTE)dhcpSendMessage + DHCP_SEND_MESSAGE_SIZE;
  461. Option = (LPOPTION) DhcpAppendMagicCookie( (LPBYTE) Option, OptionEnd );
  462. //
  463. // Append OPTIONS.
  464. //
  465. messageType = DHCP_OFFER_MESSAGE;
  466. Option = DhcpAppendOption(
  467. Option,
  468. OPTION_MESSAGE_TYPE,
  469. &messageType,
  470. 1,
  471. OptionEnd
  472. );
  473. Option = DhcpAppendOption(
  474. Option,
  475. OPTION_SERVER_IDENTIFIER,
  476. &ipaddr,
  477. sizeof(ipaddr),
  478. OptionEnd );
  479. Option = DhcpAppendOption(
  480. Option,
  481. OPTION_CLIENT_CLASS_INFO,
  482. "PXEClient",
  483. 9,
  484. OptionEnd
  485. );
  486. //
  487. // Finally, add client requested parameters.
  488. //
  489. if ( DhcpOptions->ParameterRequestList != NULL ) {
  490. Option = AppendClientRequestedParameters(
  491. 0,
  492. 0,
  493. DhcpOptions->ParameterRequestList,
  494. DhcpOptions->ParameterRequestListLength,
  495. Option,
  496. OptionEnd,
  497. DhcpOptions->ClassIdentifier,
  498. DhcpOptions->ClassIdentifierLength,
  499. FALSE
  500. );
  501. }
  502. Option = DhcpAppendOption(
  503. Option,
  504. OPTION_END,
  505. NULL,
  506. 0,
  507. OptionEnd
  508. );
  509. RequestContext->SendMessageSize = (DWORD)((LPBYTE)Option - (LPBYTE)dhcpSendMessage);
  510. BinlAssert( RequestContext->SendMessageSize <= DHCP_SEND_MESSAGE_SIZE );
  511. Error = ERROR_SUCCESS;
  512. Cleanup:
  513. if ( pMachineInfo ) {
  514. BinlDoneWithCacheEntry( pMachineInfo, FALSE );
  515. }
  516. if( Error != ERROR_SUCCESS ) {
  517. BinlPrintDbg(( DEBUG_STOC, "!! Error 0x%08x - DhcpDiscover failed.\n", Error ));
  518. }
  519. return( Error );
  520. }
  521. DWORD
  522. ProcessBinlRequestInDhcp(
  523. LPDHCP_MESSAGE DhcpReceiveMessage,
  524. LPDHCP_SERVER_OPTIONS DhcpOptions,
  525. PCHAR HostName,
  526. PCHAR BootFileName,
  527. DHCP_IP_ADDRESS *BootstrapServerAddress,
  528. LPOPTION *Option,
  529. PBYTE OptionEnd
  530. )
  531. /*++
  532. Routine Description:
  533. This function will create the response message if necessary.
  534. Arguments:
  535. dhcpOptions - Interesting options extracted from the request.
  536. Return Value:
  537. Windows Error.
  538. --*/
  539. {
  540. DWORD Error;
  541. PMACHINE_INFO pMachineInfo = NULL;
  542. BOOLEAN includePXE = TRUE;
  543. DHCP_IP_ADDRESS ipaddr;
  544. UCHAR Guid[BINL_GUID_LENGTH];
  545. DWORD GuidLength;
  546. TraceFunc("ProcessBinlRequestInDhcp( )\n" );
  547. if ( !AnswerRequests ) {
  548. BinlPrint((DEBUG_OPTIONS, "Client ignored - Not answering requests (AnswerRequests == FALSE)\n" ));
  549. return ERROR_BINL_INVALID_BINL_CLIENT;
  550. }
  551. if (BinlGlobalAuthorized == FALSE) {
  552. BinlPrint((DEBUG_ROGUE, "BINL has not passed rogue detection. Ignoring packet.\n" ));
  553. //
  554. // We'll possibly log an event here since we don't log an event
  555. // at startup saying what our rogue state is.
  556. //
  557. LogCurrentRogueState( TRUE );
  558. return ERROR_BINL_INVALID_BINL_CLIENT;
  559. }
  560. //
  561. // If a cache entry is found here, then it will be marked as InProgress as
  562. // a side effect of finding it. We need to call BinlDoneWithCacheEntry
  563. // when we're done with the entry.
  564. //
  565. Error = GetGuidFromPacket( DhcpReceiveMessage,
  566. DhcpOptions,
  567. Guid,
  568. &GuidLength,
  569. &pMachineInfo
  570. );
  571. if (Error != ERROR_SUCCESS) {
  572. goto Cleanup;
  573. }
  574. if (pMachineInfo->HostName == NULL) {
  575. Error = ERROR_BINL_INVALID_BINL_CLIENT;
  576. goto Cleanup;
  577. }
  578. wcstombs( HostName, pMachineInfo->HostName, wcslen(pMachineInfo->HostName) + 1);
  579. wcstombs( BootFileName, pMachineInfo->BootFileName, wcslen(pMachineInfo->BootFileName) + 1);
  580. BinlPrintDbg(( DEBUG_MISC, "HostName: %s\n", HostName ));
  581. BinlPrintDbg(( DEBUG_MISC, "BootFileName: %s\n", BootFileName ));
  582. //
  583. // if the server is our own, then the machineInfo->HostAddress will be
  584. // 0 and the DHCP server will fill in the correct one for us so long as
  585. // we return success.
  586. //
  587. memcpy( BootstrapServerAddress,
  588. &pMachineInfo->HostAddress,
  589. sizeof( DHCP_IP_ADDRESS ) );
  590. if (DhcpOptions->GuidLength != 0) {
  591. *Option = DhcpAppendOption(
  592. *Option,
  593. OPTION_CLIENT_GUID,
  594. DhcpOptions->Guid,
  595. DhcpOptions->GuidLength,
  596. OptionEnd );
  597. } else {
  598. UCHAR TmpBuffer[17];
  599. TmpBuffer[0] = '\0';
  600. memcpy(TmpBuffer + 1, Guid, GuidLength);
  601. *Option = DhcpAppendOption(
  602. *Option,
  603. OPTION_CLIENT_GUID,
  604. TmpBuffer,
  605. 17,
  606. OptionEnd );
  607. }
  608. //
  609. // check if OPTION_CLIENT_CLASS_INFO is already specified, if so, then
  610. // don't put PXEClient in again
  611. //
  612. if (DhcpOptions->ParameterRequestList != NULL) {
  613. LPBYTE requestList = DhcpOptions->ParameterRequestList;
  614. ULONG listLength = DhcpOptions->ParameterRequestListLength;
  615. while (listLength > 0) {
  616. if (*requestList == OPTION_CLIENT_CLASS_INFO) {
  617. includePXE = FALSE;
  618. break;
  619. }
  620. listLength--;
  621. requestList++;
  622. }
  623. }
  624. if (includePXE) {
  625. *Option = DhcpAppendOption(
  626. *Option,
  627. OPTION_CLIENT_CLASS_INFO,
  628. "PXEClient",
  629. 9,
  630. OptionEnd
  631. );
  632. }
  633. Error = ERROR_SUCCESS;
  634. Cleanup:
  635. if (pMachineInfo != NULL) {
  636. BinlDoneWithCacheEntry( pMachineInfo, FALSE );
  637. }
  638. if( Error != ERROR_SUCCESS ) {
  639. BinlPrintDbg(( DEBUG_STOC, "!! Error 0x%08x - BINL Request failed.\n", Error ));
  640. }
  641. return( Error );
  642. }
  643. DWORD
  644. ProcessBinlRequest(
  645. LPBINL_REQUEST_CONTEXT RequestContext,
  646. LPDHCP_SERVER_OPTIONS DhcpOptions
  647. )
  648. /*++
  649. Routine Description:
  650. This function will create the response message if necessary.
  651. Arguments:
  652. RequestContext - A pointer to the BinlRequestContext block for
  653. this request.
  654. dhcpOptions - Interesting options extracted from the request.
  655. Return Value:
  656. Windows Error.
  657. --*/
  658. {
  659. DWORD Error;
  660. LPDHCP_MESSAGE dhcpReceiveMessage;
  661. LPDHCP_MESSAGE dhcpSendMessage;
  662. BYTE messageType;
  663. LPOPTION Option;
  664. LPBYTE OptionEnd;
  665. PMACHINE_INFO pMachineInfo = NULL;
  666. UCHAR Guid[ BINL_GUID_LENGTH ];
  667. DHCP_IP_ADDRESS ipaddr;
  668. DHCP_IP_ADDRESS boostrapIpAddr;
  669. TraceFunc("ProcessBinlRequest( )\n" );
  670. #if DBG
  671. if ( BinlRepeatSleep )
  672. {
  673. BinlPrintDbg((DEBUG_STOC, "Delay response %u milliseconds.\n", BinlRepeatSleep ));
  674. Sleep( BinlRepeatSleep );
  675. BinlPrintDbg((DEBUG_STOC, "Awakening from sleep...\n" ));
  676. }
  677. #endif // DBG
  678. dhcpReceiveMessage = (LPDHCP_MESSAGE) RequestContext->ReceiveBuffer;
  679. //
  680. // If the client specified a server identifier option, we should
  681. // drop this packet unless the identified server is this one.
  682. //
  683. ipaddr = BinlGetMyNetworkAddress( RequestContext );
  684. if ( ipaddr == 0 ) {
  685. Error = ERROR_BINL_INVALID_BINL_CLIENT;
  686. goto Cleanup;
  687. }
  688. if ( DhcpOptions->Server != NULL ) {
  689. if ( *DhcpOptions->Server != ipaddr ) {
  690. Error = ERROR_BINL_INVALID_BINL_CLIENT;
  691. goto Cleanup;
  692. }
  693. }
  694. //
  695. // If a cacheEntry is found here, then it will be marked as InProgress as
  696. // a side effect of finding it. We need to call BinlDoneWithCacheEntry
  697. // when we're done with the entry.
  698. //
  699. Error = GetGuidFromPacket( dhcpReceiveMessage,
  700. DhcpOptions,
  701. Guid,
  702. NULL,
  703. &pMachineInfo
  704. );
  705. if (Error != ERROR_SUCCESS) {
  706. goto Cleanup;
  707. }
  708. //
  709. // Generate and send a reply.
  710. //
  711. dhcpReceiveMessage->BootFileName[ BOOT_FILE_SIZE - 1 ] = '\0';
  712. dhcpSendMessage = (LPDHCP_MESSAGE) RequestContext->SendBuffer;
  713. RtlZeroMemory( RequestContext->SendBuffer, DHCP_SEND_MESSAGE_SIZE );
  714. dhcpSendMessage->Operation = BOOT_REPLY;
  715. dhcpSendMessage->TransactionID = dhcpReceiveMessage->TransactionID;
  716. dhcpSendMessage->ClientIpAddress = dhcpReceiveMessage->ClientIpAddress;
  717. dhcpSendMessage->YourIpAddress = dhcpReceiveMessage->YourIpAddress;
  718. dhcpSendMessage->RelayAgentIpAddress = dhcpReceiveMessage->RelayAgentIpAddress;
  719. dhcpSendMessage->Reserved = dhcpReceiveMessage->Reserved;
  720. dhcpSendMessage->HardwareAddressType = dhcpReceiveMessage->HardwareAddressType;
  721. dhcpSendMessage->HardwareAddressLength = dhcpReceiveMessage->HardwareAddressLength;
  722. RtlCopyMemory(dhcpSendMessage->HardwareAddress,
  723. dhcpReceiveMessage->HardwareAddress,
  724. dhcpReceiveMessage->HardwareAddressLength );
  725. if (pMachineInfo->HostName == NULL) {
  726. Error = ERROR_BINL_INVALID_BINL_CLIENT;
  727. goto Cleanup;
  728. }
  729. // Comparing BYTE count to CHAR count
  730. BinlAssert( sizeof( dhcpSendMessage->HostName ) >= wcslen( pMachineInfo->HostName ) );
  731. BinlAssert( sizeof( dhcpSendMessage->BootFileName ) >= wcslen( pMachineInfo->BootFileName ) );
  732. wcstombs( dhcpSendMessage->HostName, pMachineInfo->HostName, wcslen(pMachineInfo->HostName) + 1);
  733. wcstombs( dhcpSendMessage->BootFileName, pMachineInfo->BootFileName, wcslen(pMachineInfo->BootFileName) + 1);
  734. //
  735. // if the machineinfo->HostAddress is zero, then that means the hostname
  736. // is the same as ours. we therefore slap in our own ipaddress in.
  737. //
  738. boostrapIpAddr = pMachineInfo->HostAddress;
  739. if (boostrapIpAddr == 0) {
  740. boostrapIpAddr = ipaddr;
  741. }
  742. dhcpSendMessage->BootstrapServerAddress = boostrapIpAddr;
  743. BinlPrintDbg(( DEBUG_MISC, "HostName: %s\n", dhcpSendMessage->HostName ));
  744. BinlPrintDbg(( DEBUG_MISC, "HostAddress: %u.%u.%u.%u\n",
  745. dhcpSendMessage->BootstrapServerAddress & 0xFF,
  746. (dhcpSendMessage->BootstrapServerAddress >> 8) & 0xFF,
  747. (dhcpSendMessage->BootstrapServerAddress >> 16) & 0xFF,
  748. (dhcpSendMessage->BootstrapServerAddress >> 24) & 0xFF ));
  749. BinlPrintDbg(( DEBUG_MISC, "BootFileName: %s\n", dhcpSendMessage->BootFileName ));
  750. Option = &dhcpSendMessage->Option;
  751. OptionEnd = (LPBYTE)dhcpSendMessage + DHCP_SEND_MESSAGE_SIZE;
  752. Option = (LPOPTION) DhcpAppendMagicCookie( (LPBYTE) Option, OptionEnd );
  753. //
  754. // Append OPTIONS.
  755. //
  756. messageType = DHCP_ACK_MESSAGE;
  757. Option = DhcpAppendOption(
  758. Option,
  759. OPTION_MESSAGE_TYPE,
  760. &messageType,
  761. 1,
  762. OptionEnd
  763. );
  764. Option = DhcpAppendOption(
  765. Option,
  766. OPTION_SERVER_IDENTIFIER,
  767. &ipaddr,
  768. sizeof(ipaddr),
  769. OptionEnd );
  770. if (DhcpOptions->GuidLength != 0) {
  771. Option = DhcpAppendOption(
  772. Option,
  773. OPTION_CLIENT_GUID,
  774. DhcpOptions->Guid,
  775. (UCHAR)DhcpOptions->GuidLength,
  776. OptionEnd );
  777. } else {
  778. UCHAR TmpBuffer[17];
  779. TmpBuffer[0] = '\0';
  780. memcpy(TmpBuffer + 1, pMachineInfo->Guid, BINL_GUID_LENGTH);
  781. Option = DhcpAppendOption(
  782. Option,
  783. OPTION_CLIENT_GUID,
  784. TmpBuffer,
  785. 17,
  786. OptionEnd );
  787. }
  788. Option = DhcpAppendOption(
  789. Option,
  790. OPTION_CLIENT_CLASS_INFO,
  791. "PXEClient",
  792. 9,
  793. OptionEnd
  794. );
  795. //
  796. // Finally, add client requested parameters.
  797. //
  798. if ( DhcpOptions->ParameterRequestList != NULL ) {
  799. Option = AppendClientRequestedParameters(
  800. 0,
  801. 0,
  802. DhcpOptions->ParameterRequestList,
  803. DhcpOptions->ParameterRequestListLength,
  804. Option,
  805. OptionEnd,
  806. DhcpOptions->ClassIdentifier,
  807. DhcpOptions->ClassIdentifierLength,
  808. FALSE
  809. );
  810. }
  811. Option = DhcpAppendOption(
  812. Option,
  813. OPTION_END,
  814. NULL,
  815. 0,
  816. OptionEnd
  817. );
  818. RequestContext->SendMessageSize = (DWORD)((LPBYTE)Option - (LPBYTE)dhcpSendMessage);
  819. BinlAssert( RequestContext->SendMessageSize <= DHCP_SEND_MESSAGE_SIZE );
  820. Error = ERROR_SUCCESS;
  821. Cleanup:
  822. if ( pMachineInfo ) {
  823. BinlDoneWithCacheEntry( pMachineInfo, FALSE );
  824. }
  825. if( Error != ERROR_SUCCESS ) {
  826. BinlPrintDbg(( DEBUG_STOC, "!! Error 0x%08x - BINL Request failed.\n", Error ));
  827. }
  828. return( Error );
  829. }
  830. DWORD
  831. ProcessBinlInform(
  832. IN LPBINL_REQUEST_CONTEXT RequestContext,
  833. IN LPDHCP_SERVER_OPTIONS DhcpOptions
  834. )
  835. /*++
  836. Routine Description:
  837. This function will create the response message to the inform packet iff
  838. the query is asking for our domain name.
  839. Arguments:
  840. RequestContext - A pointer to the BinlRequestContext block for
  841. this request.
  842. dhcpOptions - Interesting options extracted from the request.
  843. Return Value:
  844. Windows Error.
  845. --*/
  846. {
  847. DWORD Error;
  848. LPDHCP_MESSAGE dhcpReceiveMessage;
  849. LPDHCP_MESSAGE dhcpSendMessage;
  850. LPOPTION Option;
  851. LPBYTE OptionEnd;
  852. PCHAR domain = NULL;
  853. DHCP_IP_ADDRESS ipaddr;
  854. TraceFunc("ProcessBinlInform( )\n" );
  855. dhcpReceiveMessage = (LPDHCP_MESSAGE)RequestContext->ReceiveBuffer;
  856. dhcpSendMessage = (LPDHCP_MESSAGE)RequestContext->SendBuffer;
  857. ipaddr = BinlGetMyNetworkAddress( RequestContext );
  858. if ( ipaddr == 0 ) {
  859. Error = ERROR_BINL_INVALID_BINL_CLIENT;
  860. goto exit_inform;
  861. }
  862. if ( ! DhcpOptions->DSDomainNameRequested ) {
  863. BinlPrintDbg((DEBUG_STOC, "Ignoring inform as no domain name option present.\n"));
  864. Error = ERROR_BINL_INVALID_BINL_CLIENT;
  865. goto exit_inform;
  866. }
  867. domain = GetDhcpDomainName();
  868. if (domain == NULL) {
  869. BinlPrintDbg((DEBUG_STOC, "Couldn't get domain name!\n"));
  870. Error = ERROR_BINL_INVALID_BINL_CLIENT;
  871. goto exit_inform;
  872. }
  873. // if the client IP address is not zero, we may AV in dhcpssvc because
  874. // it updates a global counter tracking informs. Always have this as 0.
  875. Option = FormatDhcpInformAck( // Here come the actual formatting of the ack!
  876. dhcpReceiveMessage,
  877. dhcpSendMessage,
  878. 0, // on a ack to an inform query for name, IP address not needed.
  879. ipaddr
  880. );
  881. OptionEnd = (LPBYTE)dhcpSendMessage + DHCP_SEND_MESSAGE_SIZE;
  882. // our enterprise name was requested, append it
  883. Option = DhcpAppendEnterpriseName(
  884. Option,
  885. domain,
  886. OptionEnd
  887. );
  888. // also, make the server send out a broadcast: if someone is using a bad
  889. // ipaddr, we should make sure we reach him
  890. dhcpSendMessage->Reserved = dhcpReceiveMessage->Reserved = htons(DHCP_BROADCAST);
  891. //
  892. // Finally, add client requested parameters.
  893. //
  894. if ( DhcpOptions->ParameterRequestList != NULL ) {
  895. Option = AppendClientRequestedParameters(
  896. 0,
  897. 0,
  898. DhcpOptions->ParameterRequestList,
  899. DhcpOptions->ParameterRequestListLength,
  900. Option,
  901. OptionEnd,
  902. DhcpOptions->ClassIdentifier,
  903. DhcpOptions->ClassIdentifierLength,
  904. FALSE
  905. );
  906. }
  907. Option = DhcpAppendOption(
  908. Option,
  909. OPTION_END,
  910. NULL,
  911. 0,
  912. OptionEnd
  913. );
  914. RequestContext->SendMessageSize = (DWORD)((LPBYTE)Option - (LPBYTE)dhcpSendMessage);
  915. BinlAssert( RequestContext->SendMessageSize <= DHCP_SEND_MESSAGE_SIZE );
  916. Error = ERROR_SUCCESS;
  917. exit_inform:
  918. if (domain != NULL) {
  919. LocalFree( domain );
  920. }
  921. return Error;
  922. }
  923. LPOPTION
  924. ConsiderAppendingOption(
  925. DHCP_IP_ADDRESS IpAddress,
  926. DHCP_IP_ADDRESS SubnetMask,
  927. LPOPTION Option,
  928. BYTE OptionType,
  929. LPBYTE OptionEnd,
  930. CHAR *ClassIdentifier,
  931. DWORD ClassIdentifierLength,
  932. BOOL fSwitchedSubnet
  933. )
  934. /*++
  935. Routine Description:
  936. This function conditionally appends an option value to a response
  937. message. The option is appended if the server has a valid value
  938. to append.
  939. Arguments:
  940. IpAddress - The IP address of the client.
  941. SubnetMask - The subnet mask of the client.
  942. Option - A pointer to the place in the message buffer to append the
  943. option.
  944. OptionType - The option number to consider appending.
  945. OptionEnd - End of Option Buffer
  946. Return Value:
  947. A pointer to end of the appended data.
  948. --*/
  949. {
  950. LPBYTE optionValue = NULL;
  951. DWORD optionSize;
  952. DWORD status;
  953. DWORD dwUnused;
  954. TraceFunc( "ConsiderAppendingOption( )\n" );
  955. switch ( OptionType ) {
  956. //
  957. // Options already handled.
  958. //
  959. case OPTION_SUBNET_MASK:
  960. case OPTION_REQUESTED_ADDRESS:
  961. case OPTION_LEASE_TIME:
  962. case OPTION_OK_TO_OVERLAY:
  963. case OPTION_MESSAGE_TYPE:
  964. case OPTION_RENEWAL_TIME:
  965. case OPTION_REBIND_TIME:
  966. case OPTION_CLIENT_CLASS_INFO:
  967. case OPTION_VENDOR_SPEC_INFO:
  968. //
  969. // Options it is illegal to ask for.
  970. //
  971. case OPTION_PAD:
  972. case OPTION_PARAMETER_REQUEST_LIST:
  973. case OPTION_END:
  974. // Options for DHCP server, not for BINL
  975. case OPTION_ROUTER_ADDRESS:
  976. BinlPrintDbg(( DEBUG_ERRORS,
  977. "Unrecognized option %d\n", OptionType));
  978. default:
  979. break;
  980. }
  981. return Option;
  982. }
  983. LPOPTION
  984. AppendClientRequestedParameters(
  985. DHCP_IP_ADDRESS IpAddress,
  986. DHCP_IP_ADDRESS SubnetMask,
  987. LPBYTE RequestedList,
  988. DWORD ListLength,
  989. LPOPTION Option,
  990. LPBYTE OptionEnd,
  991. CHAR *ClassIdentifier,
  992. DWORD ClassIdentifierLength,
  993. BOOL fSwitchedSubnet
  994. )
  995. /*++
  996. Routine Description:
  997. Arguments:
  998. Return Value:
  999. A pointer to the end of appended data.
  1000. --*/
  1001. {
  1002. while ( ListLength > 0) {
  1003. Option = ConsiderAppendingOption(
  1004. IpAddress,
  1005. SubnetMask,
  1006. Option,
  1007. *RequestedList,
  1008. OptionEnd,
  1009. ClassIdentifier,
  1010. ClassIdentifierLength,
  1011. fSwitchedSubnet
  1012. );
  1013. ListLength--;
  1014. RequestedList++;
  1015. }
  1016. return Option;
  1017. }
  1018. DWORD
  1019. RecognizeClient(
  1020. PUCHAR pGuid,
  1021. PMACHINE_INFO * ppMachineInfo,
  1022. DWORD dwRequestedInfo,
  1023. ULONG SecondsSinceBoot,
  1024. USHORT SystemArchitecture
  1025. )
  1026. /*++
  1027. Routine Description:
  1028. This function only return ERROR_SUCCESS if we need to process the message
  1029. from this client. It may optionally return a cache entry if we actually
  1030. go off to the DS to get the entry.
  1031. Arguments:
  1032. Guid - Client identifier, sent to us by them.
  1033. SecondsSinceBoot - from the client. If we don't know this client and
  1034. this value is small then maybe this client is owned by another BINL
  1035. server. Give the other server time to respond before we send
  1036. OSChooser.
  1037. This gets around the problem (mostly) of two BINL servers that are
  1038. talking to two different DCs with a replication delay between them
  1039. where the client gets sent OSCHOOSER multiple times.
  1040. Alas, if DHCP is running on the same box and we're multihomed, we
  1041. can't delay as that will force the client to go to 4011. If the
  1042. client does that, then we'll probably return the wrong address.
  1043. ppMachineInfo - what we found. May be null if we didn't actually go off to
  1044. the DS.
  1045. SystemArchitecture - architecture for the client
  1046. Return Value:
  1047. --*/
  1048. {
  1049. HKEY KeyHandle;
  1050. DWORD Error;
  1051. BinlAssertMsg(dwRequestedInfo == (MI_HOSTNAME | MI_BOOTFILENAME),
  1052. "!! You must modify RecognizeClient() to generate new data\n" );
  1053. BinlAssert(ppMachineInfo);
  1054. TraceFunc( "RecognizeClient( )\n" );
  1055. //
  1056. // Attempt to get the boot parameters. This might fail if
  1057. // the server can't handle any more clients.
  1058. //
  1059. if ( AnswerOnlyValidClients ) {
  1060. //
  1061. // if we're only responding to existing clients, then call off to
  1062. // the DS to get the info.
  1063. //
  1064. Error = GetBootParameters( pGuid,
  1065. ppMachineInfo,
  1066. dwRequestedInfo,
  1067. SystemArchitecture,
  1068. FALSE );
  1069. } else {
  1070. //
  1071. // if we are answering new clients but only if it's after a
  1072. // certain timeout, then call off to the DS to get the info.
  1073. //
  1074. // Allow OSCHOOSER as a valid response, since AnswerOnlyValidClients is FALSE
  1075. //
  1076. Error = GetBootParameters( pGuid,
  1077. ppMachineInfo,
  1078. dwRequestedInfo,
  1079. SystemArchitecture,
  1080. (BOOLEAN) (SecondsSinceBoot >= BinlMinDelayResponseForNewClients) );
  1081. }
  1082. if ( Error == ERROR_SUCCESS ) {
  1083. BinlPrint((DEBUG_OPTIONS, "Recognizing client.\n" ));
  1084. BinlAssert( *ppMachineInfo != NULL );
  1085. if ( (*ppMachineInfo)->MyClient == FALSE ) {
  1086. //
  1087. // the cache entry is telling us not to handle this client.
  1088. //
  1089. BinlPrint((DEBUG_OPTIONS, "Binl cache entry says not to respond.\n" ));
  1090. Error = ERROR_BINL_INVALID_BINL_CLIENT;
  1091. BinlDoneWithCacheEntry( *ppMachineInfo, FALSE );
  1092. *ppMachineInfo = NULL;
  1093. }
  1094. } else {
  1095. if ( AnswerOnlyValidClients ) {
  1096. BinlPrint((DEBUG_OPTIONS, "Client ignored - Not answering for unknown clients (AnswerOnlyValid TRUE)\n" ));
  1097. } else {
  1098. BinlPrint((DEBUG_OPTIONS, "Client ignored - Not answering requests from boot %u < %u\n",
  1099. SecondsSinceBoot,
  1100. BinlMinDelayResponseForNewClients
  1101. ));
  1102. }
  1103. }
  1104. return Error;
  1105. }
  1106. DWORD
  1107. UpdateAccount(
  1108. PCLIENT_STATE ClientState,
  1109. PMACHINE_INFO pMachineInfo,
  1110. BOOL fCreate
  1111. )
  1112. /*++
  1113. Routine Description:
  1114. Create a new computer object. BINL must impersonate the client so that the
  1115. appropriate access checks are performed on the DS.
  1116. Arguments:
  1117. LdapHandle - User credentially created LDAP connection
  1118. pMachineInfo - Information to be used to populate the new MAO
  1119. Return Value:
  1120. Win32 error code or ERROR_SUCCESS.
  1121. --*/
  1122. {
  1123. WCHAR BootFilePath[MAX_PATH];
  1124. ULONG LdapError = LDAP_SUCCESS; // not returned
  1125. DWORD Error = ERROR_SUCCESS; // this is the returned ERROR_BINL code
  1126. ULONG iModCount, i,q;
  1127. ULONG LdapMessageId;
  1128. ULONG LdapMessageType;
  1129. PLDAPMessage LdapMessage = NULL;
  1130. BOOLEAN Impersonating = FALSE;
  1131. LDAP_BERVAL guid_attr_value;
  1132. PLDAP_BERVAL guid_attr_values[2];
  1133. LDAP_BERVAL password_attr_value;
  1134. PLDAP_BERVAL password_attr_values[2];
  1135. DWORD dwRequiredFlags = MI_SAMNAME
  1136. | MI_BOOTFILENAME
  1137. | MI_HOSTNAME
  1138. | MI_SETUPPATH
  1139. | MI_PASSWORD;
  1140. PWCHAR attr_values[6][2];
  1141. PLDAPMod ldap_mods[6];
  1142. LDAPMod SamAccountName;
  1143. LDAPMod ObjectTypeComputer;
  1144. LDAPMod FilePath;
  1145. LDAPMod SetupPathMod;
  1146. LDAPMod UserAccountControl;
  1147. LDAPMod UnicodePwd;
  1148. LDAPMod NicGuid;
  1149. BOOLEAN invalidateCache = FALSE;
  1150. BOOLEAN updateCache = FALSE;
  1151. TraceFunc( "UpdateAccount( )\n" );
  1152. //
  1153. // First impersonate the client.
  1154. //
  1155. Error = OscImpersonate(ClientState);
  1156. if (Error != ERROR_SUCCESS) {
  1157. BinlPrintDbg((DEBUG_ERRORS,
  1158. "UpdateAccount: OscImpersonate failed %lx\n", Error));
  1159. goto Cleanup;
  1160. }
  1161. tryagain:
  1162. Impersonating = TRUE;
  1163. //
  1164. // now initialize all of the properties we want to set on the MAO.
  1165. //
  1166. // Make sure we have all the information we need.
  1167. if ( ! (pMachineInfo->dwFlags & MI_MACHINEDN) || pMachineInfo->MachineDN == NULL ) {
  1168. BinlAssertMsg( 0, "Missing the Machine's DN" );
  1169. OscAddVariableA( ClientState, "SUBERROR", "MACHINEDN" );
  1170. Error = ERROR_BINL_MISSING_VARIABLE;
  1171. goto Cleanup;
  1172. }
  1173. BinlAssert( !fCreate || (( pMachineInfo->dwFlags & dwRequiredFlags ) == dwRequiredFlags ) );
  1174. #if DBG
  1175. // We must have both of these or none of these.
  1176. if ( ( pMachineInfo->dwFlags & MI_HOSTNAME ) || ( pMachineInfo->dwFlags & MI_BOOTFILENAME ) ) {
  1177. BinlAssert( ( pMachineInfo->dwFlags & (MI_HOSTNAME | MI_BOOTFILENAME) ) == (MI_HOSTNAME | MI_BOOTFILENAME) );
  1178. }
  1179. #endif
  1180. iModCount = 0;
  1181. if ( AssignNewClientsToServer &&
  1182. (pMachineInfo->dwFlags & (MI_HOSTNAME | MI_BOOTFILENAME)) )
  1183. {
  1184. if ( _snwprintf( BootFilePath,
  1185. sizeof(BootFilePath) / sizeof(BootFilePath[0]),
  1186. L"%ws\\%ws",
  1187. pMachineInfo->HostName,
  1188. pMachineInfo->BootFileName
  1189. ) == -1 ) {
  1190. Error = ERROR_BAD_PATHNAME;
  1191. goto Cleanup;
  1192. }
  1193. attr_values[2][0] = BootFilePath;
  1194. attr_values[2][1] = NULL;
  1195. FilePath.mod_op = 0;
  1196. FilePath.mod_type = L"netbootMachineFilePath";
  1197. FilePath.mod_values = attr_values[2];
  1198. ldap_mods[iModCount++] = &FilePath;
  1199. }
  1200. if ( pMachineInfo->dwFlags & MI_SETUPPATH ) {
  1201. attr_values[3][0] = pMachineInfo->SetupPath;
  1202. attr_values[3][1] = NULL;
  1203. SetupPathMod.mod_op = 0;
  1204. SetupPathMod.mod_type = L"netbootInitialization";
  1205. SetupPathMod.mod_values = attr_values[3];
  1206. ldap_mods[iModCount++] = &SetupPathMod;
  1207. }
  1208. if ( pMachineInfo->dwFlags & MI_GUID ) {
  1209. guid_attr_values[0] = &guid_attr_value;
  1210. guid_attr_values[1] = NULL;
  1211. guid_attr_value.bv_val = pMachineInfo->Guid;
  1212. guid_attr_value.bv_len = BINL_GUID_LENGTH;
  1213. NicGuid.mod_op = LDAP_MOD_BVALUES;
  1214. NicGuid.mod_type = L"netbootGUID";
  1215. NicGuid.mod_bvalues = guid_attr_values;
  1216. ldap_mods[iModCount++] = &NicGuid;
  1217. }
  1218. if ( fCreate && ( pMachineInfo->dwFlags & MI_SAMNAME ) ) {
  1219. attr_values[0][0] = pMachineInfo->SamName;
  1220. attr_values[0][1] = NULL;
  1221. SamAccountName.mod_op = 0;
  1222. SamAccountName.mod_type = L"sAMAccountName";
  1223. SamAccountName.mod_values = attr_values[0];
  1224. ldap_mods[iModCount++] = &SamAccountName;
  1225. }
  1226. attr_values[4][0] = L"4096"; // 0x1000 -- workstation trust account, enabled
  1227. attr_values[4][1] = NULL;
  1228. UserAccountControl.mod_op = 0;
  1229. UserAccountControl.mod_type = L"userAccountControl";
  1230. UserAccountControl.mod_values = attr_values[4];
  1231. ldap_mods[iModCount++] = &UserAccountControl;
  1232. //
  1233. // if we're creating the MAO, then we need to specify the object type
  1234. // as a computer object
  1235. //
  1236. if ( fCreate ) {
  1237. attr_values[1][0] = L"Computer";
  1238. attr_values[1][1] = NULL;
  1239. ObjectTypeComputer.mod_op = 0;
  1240. ObjectTypeComputer.mod_type = L"objectClass";
  1241. ObjectTypeComputer.mod_values = attr_values[1];
  1242. ldap_mods[iModCount++] = &ObjectTypeComputer;
  1243. }
  1244. //
  1245. // Set the operation type depending on the create or modify flag
  1246. //
  1247. for ( i = 0 ; i < iModCount; i++ )
  1248. {
  1249. if ( fCreate ) {
  1250. ldap_mods[i]->mod_op |= LDAP_MOD_ADD;
  1251. } else {
  1252. ldap_mods[i]->mod_op |= LDAP_MOD_REPLACE;
  1253. }
  1254. }
  1255. ldap_mods[iModCount] = NULL; // terminate list
  1256. //
  1257. // The properties are initialized, so now either create or modify the MAO.
  1258. //
  1259. if ( fCreate || iModCount ) {
  1260. if ( fCreate ) {
  1261. BinlPrintDbg((DEBUG_OSC, "UpdateAccount() Creating a new MAO\n" ));
  1262. #if DBG
  1263. for (q = 0;q < iModCount; q++) {
  1264. BinlPrintDbg(( DEBUG_OSC, "LDAP Prop %x: Type: %S Value: %S",
  1265. q,
  1266. ldap_mods[q]->mod_type,
  1267. *ldap_mods[q]->mod_vals.modv_strvals ));
  1268. }
  1269. #endif
  1270. //
  1271. // synchronously Create the object.
  1272. //
  1273. LdapMessageId = ldap_add( ClientState->AuthenticatedDCLdapHandle, pMachineInfo->MachineDN, ldap_mods );
  1274. if (LdapMessageId == -1) {
  1275. Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
  1276. LdapError = LdapGetLastError();
  1277. LogLdapError( EVENT_WARNING_LDAP_ADD_ERROR,
  1278. LdapError,
  1279. ClientState->AuthenticatedDCLdapHandle
  1280. );
  1281. BinlPrintDbg(( DEBUG_ERRORS,
  1282. "CreateAccount ldap_add failed %x\n", LdapError));
  1283. goto Cleanup;
  1284. }
  1285. LdapMessageType = ldap_result(
  1286. ClientState->AuthenticatedDCLdapHandle,
  1287. LdapMessageId,
  1288. LDAP_MSG_ALL,
  1289. &BinlLdapSearchTimeout,
  1290. &LdapMessage);
  1291. if (LdapMessageType != LDAP_RES_ADD) {
  1292. BinlPrintDbg(( DEBUG_ERRORS,
  1293. "CreateAccount ldap_result returned type %lx\n", LdapMessageType));
  1294. OscAddVariableA( ClientState, "SUBERROR", "Unexpected LDAP error" );
  1295. Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
  1296. goto Cleanup;
  1297. }
  1298. LdapError = ldap_result2error(
  1299. ClientState->AuthenticatedDCLdapHandle,
  1300. LdapMessage,
  1301. 0);
  1302. if (LdapError != LDAP_SUCCESS) {
  1303. if (LdapError != LDAP_ALREADY_EXISTS) {
  1304. Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
  1305. LogLdapError( EVENT_WARNING_LDAP_ADD_ERROR,
  1306. LdapError,
  1307. ClientState->AuthenticatedDCLdapHandle
  1308. );
  1309. BinlPrintDbg(( DEBUG_ERRORS, "!!LdapError 0x%08x - UpdateAccount ldap_add_s( ) failed\n", LdapError));
  1310. goto Cleanup;
  1311. } else {
  1312. BinlPrintDbg((DEBUG_OSC, "UpdateAccount() tried to create an existing account. Try again, but modify existing MAO.\n" ));
  1313. fCreate = FALSE;
  1314. goto tryagain;
  1315. }
  1316. }
  1317. updateCache = TRUE;
  1318. } else {
  1319. //
  1320. // We don't strictly need to reset the properties below, as the
  1321. // content under the MAO should be static. But it won't really
  1322. // hurt things to try to reset in case something does change.
  1323. //
  1324. // Note that the reset of these properties may not succeed because
  1325. // the user may not have permissions to modify the MAO, depending
  1326. // on how the admin locks things down. (The admin can use GPO to
  1327. // allow the user to create MAOs but not modify the objects.)
  1328. //
  1329. //
  1330. // asynchronously reset the properties
  1331. //
  1332. BinlPrintDbg((DEBUG_OSC, "UpdateAccount() updating existing MAO\n" ));
  1333. LdapMessageId = ldap_modify( ClientState->AuthenticatedDCLdapHandle, pMachineInfo->MachineDN, ldap_mods );
  1334. if (LdapMessageId == -1) {
  1335. Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
  1336. LdapError = LdapGetLastError();
  1337. LogLdapError( EVENT_WARNING_LDAP_MODIFY_ERROR,
  1338. LdapError,
  1339. ClientState->AuthenticatedDCLdapHandle
  1340. );
  1341. BinlPrintDbg(( DEBUG_ERRORS,
  1342. "UpdateAccount ldap_modify(userAccountControl) failed %x\n", LdapError));
  1343. goto Cleanup;
  1344. }
  1345. LdapMessageType = ldap_result(
  1346. ClientState->AuthenticatedDCLdapHandle,
  1347. LdapMessageId,
  1348. LDAP_MSG_ALL,
  1349. &BinlLdapSearchTimeout,
  1350. &LdapMessage);
  1351. if (LdapMessageType != LDAP_RES_MODIFY) {
  1352. BinlPrintDbg(( DEBUG_ERRORS,
  1353. "CreateAccount ldap_result returned type %lx\n", LdapMessageType));
  1354. OscAddVariableA( ClientState, "SUBERROR", "Unexpected LDAP error" );
  1355. Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
  1356. goto Cleanup;
  1357. }
  1358. LdapError = ldap_result2error(
  1359. ClientState->AuthenticatedDCLdapHandle,
  1360. LdapMessage,
  1361. 0);
  1362. if (LdapError != LDAP_SUCCESS) {
  1363. LogLdapError( EVENT_WARNING_LDAP_MODIFY_ERROR,
  1364. LdapError,
  1365. ClientState->AuthenticatedDCLdapHandle
  1366. );
  1367. BinlPrintDbg(( DEBUG_ERRORS, "CreateAccount ldap_result2error failed %x\n", LdapError));
  1368. // if the user doesn't have the rights to change
  1369. // the properties then we'll just silently ignore the error
  1370. // (though we did just log an error for it).
  1371. if ( LdapError != LDAP_INSUFFICIENT_RIGHTS) {
  1372. Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
  1373. goto Cleanup;
  1374. }
  1375. LdapError = LDAP_SUCCESS;
  1376. }
  1377. updateCache = TRUE;
  1378. }
  1379. }
  1380. //
  1381. // if we've made it this far, we've got a MAO that's setup properly.
  1382. // Now we need to reset the account password so the domain join is
  1383. // somewhat secure.
  1384. //
  1385. if ( pMachineInfo->dwFlags & MI_PASSWORD ) {
  1386. #ifdef SET_PASSWORD_WITH_LDAP
  1387. iModCount = 0;
  1388. password_attr_values[0] = &password_attr_value;
  1389. password_attr_values[1] = NULL;
  1390. password_attr_value.bv_val = (PUCHAR) pMachineInfo->Password;
  1391. password_attr_value.bv_len = pMachineInfo->PasswordLength;
  1392. UnicodePwd.mod_op = LDAP_MOD_ADD | LDAP_MOD_BVALUES; // you always "Add" the "unicodePwd"
  1393. UnicodePwd.mod_type = L"unicodePwd";
  1394. UnicodePwd.mod_bvalues = password_attr_values;
  1395. ldap_mods[iModCount++] = &UnicodePwd;
  1396. ldap_mods[iModCount] = NULL; // terminate list
  1397. LdapError = ldap_modify_s( ClientState->AuthenticatedDCLdapHandle, pMachineInfo->MachineDN, ldap_mods );
  1398. if (LdapError != LDAP_SUCCESS) {
  1399. LogLdapError( EVENT_WARNING_LDAP_MODIFY_ERROR,
  1400. LdapError,
  1401. ClientState->AuthenticatedDCLdapHandle
  1402. );
  1403. BinlPrintDbg(( DEBUG_ERRORS, "!!LdapError 0x%08x - UpdateAccount ldap_modify_s( ) failed\n", LdapError));
  1404. goto Cleanup;
  1405. }
  1406. #else
  1407. //
  1408. // At this point we depend on LdapMessage being valid, which will
  1409. // *not* be the case if we are only setting the password. This
  1410. // breaks machine replacement for the moment.
  1411. //
  1412. BinlAssert( LdapMessage != NULL );
  1413. Error = OscUpdatePassword(
  1414. ClientState,
  1415. pMachineInfo->SamName,
  1416. pMachineInfo->Password,
  1417. ClientState->AuthenticatedDCLdapHandle,
  1418. LdapMessage);
  1419. if (Error != ERROR_SUCCESS) {
  1420. goto Cleanup;
  1421. }
  1422. #endif
  1423. }
  1424. Cleanup:
  1425. //
  1426. // Convert the LdapError to a ERROR_BINL and put the LdapError
  1427. // into SUBERROR.
  1428. //
  1429. if ( LdapError != LDAP_SUCCESS )
  1430. {
  1431. OscCreateLDAPSubError( ClientState, LdapError );
  1432. switch ( LdapError )
  1433. {
  1434. case LDAP_ALREADY_EXISTS:
  1435. Error = ERROR_BINL_DUPLICATE_MACHINE_NAME_FOUND;
  1436. break;
  1437. case LDAP_INVALID_DN_SYNTAX:
  1438. Error = ERROR_BINL_INVALID_OR_MISSING_OU;
  1439. break;
  1440. default:
  1441. Error = ERROR_BINL_FAILED_TO_CREATE_CLIENT;
  1442. break;
  1443. }
  1444. }
  1445. if ( updateCache && ( pMachineInfo->dwFlags & MI_GUID ) ) {
  1446. //
  1447. // update the cached DS information so that it is current. We do
  1448. // this because if the account is created in a child domain, we still
  1449. // have the info cached (even if it hasn't replicated to the GC yet).
  1450. //
  1451. PMACHINE_INFO pCacheEntry = NULL;
  1452. BinlCreateOrFindCacheEntry( pMachineInfo->Guid, TRUE, &pCacheEntry );
  1453. invalidateCache = FALSE;
  1454. // we don't care about the error coming back, only if a record was found.
  1455. if (pCacheEntry != NULL) {
  1456. pCacheEntry->TimeCreated = GetTickCount();
  1457. pCacheEntry->MyClient = TRUE;
  1458. pCacheEntry->EntryExists = TRUE;
  1459. if (pCacheEntry != pMachineInfo) {
  1460. memcpy( &pCacheEntry->HostAddress,
  1461. &pMachineInfo->HostAddress,
  1462. sizeof(pMachineInfo->HostAddress));
  1463. if ( pMachineInfo->Name ) {
  1464. pCacheEntry->Name = BinlStrDup( pMachineInfo->Name );
  1465. if (!pCacheEntry->Name) {
  1466. goto noMemory;
  1467. }
  1468. pCacheEntry->dwFlags |= MI_NAME_ALLOC | MI_NAME;
  1469. }
  1470. if ( pMachineInfo->MachineDN ) {
  1471. pCacheEntry->MachineDN = BinlStrDup( pMachineInfo->MachineDN );
  1472. if (!pCacheEntry->MachineDN) {
  1473. goto noMemory;
  1474. }
  1475. pCacheEntry->dwFlags |= MI_MACHINEDN_ALLOC | MI_MACHINEDN;
  1476. }
  1477. if ( pMachineInfo->SetupPath ) {
  1478. pCacheEntry->SetupPath = BinlStrDup( pMachineInfo->SetupPath );
  1479. if (!pCacheEntry->SetupPath) {
  1480. goto noMemory;
  1481. }
  1482. pCacheEntry->dwFlags |= MI_SETUPPATH_ALLOC | MI_SETUPPATH;
  1483. }
  1484. if ( pMachineInfo->HostName ) {
  1485. pCacheEntry->HostName = BinlStrDup( pMachineInfo->HostName );
  1486. if (!pCacheEntry->HostName) {
  1487. goto noMemory;
  1488. }
  1489. pCacheEntry->dwFlags |= MI_HOSTNAME_ALLOC | MI_HOSTNAME;
  1490. }
  1491. if ( pMachineInfo->SamName ) {
  1492. pCacheEntry->SamName = BinlStrDup( pMachineInfo->SamName );
  1493. if (!pCacheEntry->SamName) {
  1494. goto noMemory;
  1495. }
  1496. pCacheEntry->dwFlags |= MI_SAMNAME_ALLOC | MI_SAMNAME;
  1497. }
  1498. if ( pMachineInfo->Domain ) {
  1499. pCacheEntry->Domain = BinlStrDup( pMachineInfo->Domain );
  1500. if (!pCacheEntry->Domain) {
  1501. noMemory:
  1502. invalidateCache = TRUE;
  1503. Error = ERROR_NOT_ENOUGH_SERVER_MEMORY;
  1504. } else {
  1505. pCacheEntry->dwFlags |= MI_DOMAIN_ALLOC | MI_DOMAIN;
  1506. }
  1507. }
  1508. }
  1509. BinlDoneWithCacheEntry( pCacheEntry, invalidateCache );
  1510. }
  1511. }
  1512. if ( invalidateCache && ( pMachineInfo->dwFlags & MI_GUID ) ) {
  1513. //
  1514. // invalidate the cached DS information if we failed because it's stale.
  1515. //
  1516. PMACHINE_INFO pCacheEntry = NULL;
  1517. BinlCreateOrFindCacheEntry( pMachineInfo->Guid, FALSE, &pCacheEntry );
  1518. // we don't care about the error coming back, only if a record was found.
  1519. if ((pCacheEntry != NULL) &&
  1520. (pCacheEntry != pMachineInfo)) {
  1521. BinlDoneWithCacheEntry( pCacheEntry, TRUE );
  1522. }
  1523. }
  1524. if (LdapMessage != NULL) {
  1525. ldap_msgfree(LdapMessage);
  1526. }
  1527. if (Impersonating) {
  1528. OscRevert(ClientState);
  1529. }
  1530. return Error;
  1531. }
  1532. DWORD
  1533. BinlGenerateNewEntry(
  1534. DWORD dwRequestedInfo,
  1535. USHORT SystemArchitecture,
  1536. PMACHINE_INFO * ppMachineInfo )
  1537. {
  1538. DWORD Error = ERROR_BINL_INVALID_BINL_CLIENT;
  1539. TraceFunc( "BinlGenerateNewEntry( ... )\n" );
  1540. if ( AllowNewClients ) {
  1541. BinlPrint(( DEBUG_OPTIONS, "Server allows new clients" ));
  1542. if ( ( LimitClients == FALSE ) ||
  1543. ( CurrentClientCount < BinlMaxClients ) ) {
  1544. BinlPrint(( DEBUG_OPTIONS, " and the Server is generating the OS Chooser path response.\n" ));
  1545. if ( dwRequestedInfo & MI_HOSTNAME ) {
  1546. ULONG ulSize;
  1547. if ( (*ppMachineInfo)->dwFlags & MI_HOSTNAME_ALLOC ) {
  1548. BinlFreeMemory( (*ppMachineInfo)->HostName );
  1549. (*ppMachineInfo)->HostName = NULL;
  1550. (*ppMachineInfo)->dwFlags &= ~MI_HOSTNAME_ALLOC;
  1551. }
  1552. EnterCriticalSection( &gcsParameters );
  1553. if (BinlGlobalOurDnsName == NULL) {
  1554. LeaveCriticalSection( &gcsParameters );
  1555. return (ERROR_OUTOFMEMORY);
  1556. }
  1557. (*ppMachineInfo)->HostName = (PWCHAR) BinlAllocateMemory( ( lstrlenW( BinlGlobalOurDnsName ) + 1 ) * sizeof(WCHAR) );
  1558. if ( !(*ppMachineInfo)->HostName ) {
  1559. LeaveCriticalSection( &gcsParameters );
  1560. return (ERROR_OUTOFMEMORY);
  1561. }
  1562. lstrcpyW( (*ppMachineInfo)->HostName, BinlGlobalOurDnsName );
  1563. LeaveCriticalSection( &gcsParameters );
  1564. (*ppMachineInfo)->dwFlags |= MI_HOSTNAME_ALLOC;
  1565. (*ppMachineInfo)->dwFlags |= MI_HOSTNAME;
  1566. }
  1567. if ( dwRequestedInfo & MI_BOOTFILENAME ) {
  1568. ULONG ulSize;
  1569. PCWSTR OsChooserName = NULL;
  1570. switch ( SystemArchitecture ) {
  1571. case DHCP_OPTION_CLIENT_ARCHITECTURE_X86:
  1572. OsChooserName = IntelOSChooser;
  1573. ulSize = (wcslen(OsChooserName)+1)*sizeof(WCHAR);
  1574. break;
  1575. case DHCP_OPTION_CLIENT_ARCHITECTURE_IA64:
  1576. OsChooserName = IA64OSChooser;
  1577. ulSize = (wcslen(OsChooserName)+1)*sizeof(WCHAR);
  1578. break;
  1579. default:
  1580. BinlAssertMsg( FALSE, "UnsupportedArchitecture" );
  1581. }
  1582. if (OsChooserName) {
  1583. if ( (*ppMachineInfo)->dwFlags & MI_BOOTFILENAME_ALLOC ) {
  1584. BinlFreeMemory( (*ppMachineInfo)->BootFileName );
  1585. (*ppMachineInfo)->dwFlags &= ~MI_BOOTFILENAME_ALLOC;
  1586. }
  1587. (*ppMachineInfo)->BootFileName = BinlAllocateMemory( ulSize * sizeof(WCHAR) );
  1588. if ( !(*ppMachineInfo)->BootFileName ) {
  1589. return (ERROR_OUTOFMEMORY);
  1590. }
  1591. wcscpy((*ppMachineInfo)->BootFileName, OsChooserName);
  1592. (*ppMachineInfo)->dwFlags |= MI_BOOTFILENAME | MI_BOOTFILENAME_ALLOC;
  1593. }
  1594. }
  1595. Error = ( ((*ppMachineInfo)->dwFlags & dwRequestedInfo ) == dwRequestedInfo ?
  1596. ERROR_SUCCESS :
  1597. ERROR_BINL_FAILED_TO_INITIALIZE_CLIENT );
  1598. } else {
  1599. BinlPrint(( DEBUG_OPTIONS, "... BUT the server has reached MaxClients (%u)\n", BinlMaxClients ));
  1600. }
  1601. } else {
  1602. BinlPrint((DEBUG_OPTIONS, "Server does not allow new clients (AllowNewClients == FALSE )\n" ));
  1603. }
  1604. return Error;
  1605. }
  1606. DWORD
  1607. GetBootParameters(
  1608. PUCHAR pGuid,
  1609. PMACHINE_INFO * ppMachineInfo,
  1610. DWORD dwRequestedInfo,
  1611. USHORT SystemArchitecture,
  1612. BOOL AllowOSChooser
  1613. )
  1614. /*++
  1615. Routine Description:
  1616. Use the Directory Service to lookup an entry for this machine using Guid as
  1617. the value to lookup.
  1618. If there is no entry for this machine then return oschooser, but only
  1619. if the AllowOSChooser flag is set.
  1620. If a cache entry is returned, then the cache entry has been marked
  1621. InProgress so we have to call BinlDoneWithCacheEntry when the caller
  1622. is done with it.
  1623. Arguments:
  1624. pGuid - Supplies the machine GUID
  1625. ppMachineInfo - gets filled in with what we discovered
  1626. dwRequestedInfo - a bitmask telling us what parameters we're looking for
  1627. SystemArchitecture - architecture of the client
  1628. AllowOSChooser - signifies that we're allowed to respond to the client with
  1629. the oschooser
  1630. Return Value:
  1631. ERROR_SUCCESS or ERROR_BINL_INVALID_BINL_CLIENT or other error.
  1632. --*/
  1633. {
  1634. DWORD Error = ERROR_SUCCESS;
  1635. BOOLEAN myClient = TRUE;
  1636. BOOLEAN entryExists = FALSE;
  1637. TraceFunc( "GetBootParameters( )\n" );
  1638. BinlAssert( ppMachineInfo );
  1639. {
  1640. LPGUID GuidPtr = (LPGUID) pGuid;
  1641. BinlPrint((DEBUG_MISC, "Client Guid: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
  1642. GuidPtr->Data1, GuidPtr->Data2, GuidPtr->Data3,
  1643. GuidPtr->Data4[0], GuidPtr->Data4[1], GuidPtr->Data4[2], GuidPtr->Data4[3],
  1644. GuidPtr->Data4[4], GuidPtr->Data4[5], GuidPtr->Data4[6], GuidPtr->Data4[7] ));
  1645. }
  1646. if ( ppMachineInfo == NULL ) {
  1647. return E_OUTOFMEMORY;
  1648. }
  1649. if (*ppMachineInfo == NULL) {
  1650. //
  1651. // See if we have any entries in the cache.
  1652. // This also mark any entry found as being used.
  1653. //
  1654. Error = BinlCreateOrFindCacheEntry( pGuid, TRUE, ppMachineInfo );
  1655. if ( Error != ERROR_SUCCESS ) {
  1656. //
  1657. // if some bizarre error occurred OR if the client simply wasn't
  1658. // found and we're not sending down OS Chooser, then return the
  1659. // error here as there's no reason to query the DS.
  1660. //
  1661. if ( (Error != ERROR_BINL_INVALID_BINL_CLIENT ) ||
  1662. (AllowOSChooser == FALSE) ) {
  1663. return Error;
  1664. }
  1665. }
  1666. }
  1667. // Do we have everything we need?
  1668. if ( ( Error == ERROR_SUCCESS ) &&
  1669. (((*ppMachineInfo)->dwFlags & dwRequestedInfo) == dwRequestedInfo )) {
  1670. BinlPrint((DEBUG_MISC, "cache hit: returning success without querying ds DS\n"));
  1671. return Error; // Yes, no need to hit the DS.
  1672. }
  1673. //
  1674. // Initially search for the Computer object in the same domain as ourselves.
  1675. // This should be quick (because we are probably on a DC) and likely to work
  1676. // most of the time because the network topology will usually match the domain
  1677. // structure. If that fails then we fall back to looking at the global catalog.
  1678. //
  1679. if ( Error != ERROR_BINL_INVALID_BINL_CLIENT ) {
  1680. Error = GetBootParametersExt(
  1681. *ppMachineInfo,
  1682. dwRequestedInfo,
  1683. SystemArchitecture,
  1684. FALSE);
  1685. if ( Error == ERROR_BINL_INVALID_BINL_CLIENT ) {
  1686. Error = GetBootParametersExt(
  1687. *ppMachineInfo,
  1688. dwRequestedInfo,
  1689. SystemArchitecture,
  1690. TRUE );
  1691. }
  1692. }
  1693. if ( Error == ERROR_BINL_INVALID_BINL_CLIENT ) {
  1694. //
  1695. // Backdoor for testing/overiding the DS.
  1696. //
  1697. // If the registry has the GUID of the client, it
  1698. // overrides all the DS settings and answers anyways.
  1699. //
  1700. // NOTE: AllowNewClients must be turned on for OSChooser to
  1701. // be sent down.
  1702. //
  1703. HKEY KeyHandle;
  1704. if (AllowOSChooser == TRUE) {
  1705. //
  1706. // if the client is not found in the DS and we're allowed to
  1707. // answer new clients, then send down OSCHOOSER to get the new
  1708. // client going.
  1709. //
  1710. BinlPrint((DEBUG_MISC, "generating a new entry because AllowOSChooser is TRUE...\n"));
  1711. Error = BinlGenerateNewEntry( dwRequestedInfo, SystemArchitecture, ppMachineInfo );
  1712. if ( Error != ERROR_SUCCESS ) {
  1713. myClient = FALSE;
  1714. }
  1715. } else {
  1716. //
  1717. // We're not answering because we didn't find the client
  1718. // record but the client's SecondsSinceBoot is less than
  1719. // BinlMinDelayResponseForNewClients.
  1720. //
  1721. Error = ERROR_BINL_INVALID_BINL_CLIENT;
  1722. myClient = FALSE;
  1723. BinlPrint((DEBUG_OPTIONS, "... OS Chooser is not an option at this time... waiting...\n" ));
  1724. }
  1725. }
  1726. //
  1727. // Determine the host servers IP address iff it's not our own machine.
  1728. //
  1729. if ((Error == ERROR_SUCCESS) &&
  1730. ( (*ppMachineInfo)->dwFlags & MI_HOSTNAME )
  1731. && ( (*ppMachineInfo)->HostAddress == 0 )
  1732. && ( (*ppMachineInfo)->HostName )) {
  1733. EnterCriticalSection( &gcsParameters );
  1734. if ( (BinlGlobalOurDnsName != NULL) &&
  1735. (lstrcmpiW( BinlGlobalOurDnsName, (*ppMachineInfo)->HostName ) != 0 )) {
  1736. PCHAR machineName;
  1737. PHOSTENT host;
  1738. ULONG myMachineNameLength;
  1739. PCHAR myMachineName;
  1740. ULONG machineNameLength;
  1741. myMachineNameLength = wcslen( BinlGlobalOurDnsName ) + 1;
  1742. myMachineName = BinlAllocateMemory ( myMachineNameLength * sizeof(WCHAR) );
  1743. if ( myMachineName != NULL ) {
  1744. wcstombs(myMachineName, BinlGlobalOurDnsName, myMachineNameLength );
  1745. }
  1746. LeaveCriticalSection( &gcsParameters );
  1747. machineNameLength = wcslen((*ppMachineInfo)->HostName) + 1;
  1748. machineName = BinlAllocateMemory( machineNameLength );
  1749. //
  1750. // Only fill in the IP address if the server is different from our
  1751. // own machine. If we fail for any reason, we'll just end up using
  1752. // our own IP address.
  1753. //
  1754. if (machineName != NULL) {
  1755. wcstombs( machineName, (*ppMachineInfo)->HostName, machineNameLength );
  1756. host = gethostbyname( machineName );
  1757. if (host != NULL) {
  1758. (*ppMachineInfo)->HostAddress = *(PDHCP_IP_ADDRESS)host->h_addr;
  1759. // Adding stuff for multi-home NIC
  1760. if (myMachineName != NULL) {
  1761. PHOSTENT myhost;
  1762. int i;
  1763. myhost = gethostbyname( myMachineName );
  1764. if (myhost != NULL) {
  1765. i=0;
  1766. while (((myhost->h_addr_list)[i]) != NULL) {
  1767. if ((*((PDHCP_IP_ADDRESS)((myhost->h_addr_list)[i])))
  1768. == (*ppMachineInfo)->HostAddress) {
  1769. (*ppMachineInfo)->HostAddress = (DHCP_IP_ADDRESS)0;
  1770. break;
  1771. }
  1772. i++;
  1773. }
  1774. }
  1775. }
  1776. } else {
  1777. Error = ERROR_HOST_UNREACHABLE;
  1778. myClient = FALSE;
  1779. entryExists = TRUE;
  1780. }
  1781. BinlFreeMemory( machineName );
  1782. } else {
  1783. Error = ERROR_NOT_ENOUGH_MEMORY;
  1784. myClient = FALSE;
  1785. entryExists = TRUE;
  1786. }
  1787. if ( myMachineName != NULL ) {
  1788. BinlFreeMemory( myMachineName );
  1789. }
  1790. } else {
  1791. LeaveCriticalSection( &gcsParameters );
  1792. }
  1793. }
  1794. if (Error != ERROR_SUCCESS) {
  1795. //
  1796. // If we didn't find the record, then we mark it that we don't need
  1797. // to respond and it doesn't exist. We then mark that we're done with
  1798. // the entry since we're not passing it back to the caller.
  1799. //
  1800. (*ppMachineInfo)->MyClient = myClient;
  1801. (*ppMachineInfo)->EntryExists = entryExists;
  1802. BinlDoneWithCacheEntry( *ppMachineInfo, FALSE );
  1803. *ppMachineInfo = NULL;
  1804. } else {
  1805. //
  1806. // we've filled in the interesting fields, therefore mark that the
  1807. // entry has valid data.
  1808. //
  1809. (*ppMachineInfo)->MyClient = TRUE;
  1810. (*ppMachineInfo)->EntryExists = TRUE;
  1811. }
  1812. return Error;
  1813. }
  1814. DWORD
  1815. GetBootParametersExt(
  1816. PMACHINE_INFO pMachineInfo,
  1817. DWORD dwRequestedInfo,
  1818. USHORT SystemArchitecture,
  1819. BOOL fGlobalSearch)
  1820. /*++
  1821. Routine Description:
  1822. Use the Directory Service to lookup an entry for this machine using Guid as
  1823. the value to lookup.
  1824. If there is no entry for this machine then return oschooser
  1825. Arguments:
  1826. pMachineInfo - identifies the machine in the DS.
  1827. dwRequestedInfo - mask telling what information we should query
  1828. SystemArchitecture - architecture of the client
  1829. GlobalSearch - TRUE if GC should be used
  1830. Return Value:
  1831. ERROR_SUCCESS or ERROR_BINL_INVALID_BINL_CLIENT
  1832. --*/
  1833. {
  1834. DWORD dwErr = ERROR_BINL_INVALID_BINL_CLIENT;
  1835. PLDAP LdapHandle = NULL;
  1836. PWCHAR * Base;
  1837. DWORD LdapError;
  1838. DWORD entryCount;
  1839. DWORD ldapRetryLimit = 0;
  1840. PLDAPMessage LdapMessage;
  1841. PWCHAR * FilePath;
  1842. PWCHAR * FilePath2;
  1843. PLDAPMessage CurrentEntry;
  1844. WCHAR Filter[128];
  1845. WCHAR EscapedGuid[64];
  1846. // Paramters we want from the Computer Object
  1847. PWCHAR ComputerAttrs[7];
  1848. PDUP_GUID_DN dupDN;
  1849. TraceFunc( "GetBootParametersExt( )\n" );
  1850. pMachineInfo->dwFlags &= MI_ALL_ALLOC; // clear all but the ALLOC bits
  1851. // we get all the info, regardless of what was requested.
  1852. ComputerAttrs[0] = &L"netbootMachineFilePath";
  1853. ComputerAttrs[1] = &L"netbootInitialization";
  1854. ComputerAttrs[2] = &L"sAMAccountName";
  1855. ComputerAttrs[3] = &L"dnsHostName";
  1856. ComputerAttrs[4] = &L"distinguishedName";
  1857. ComputerAttrs[5] = &L"netbootSIFFile";
  1858. ComputerAttrs[6] = NULL;
  1859. BinlAssertMsg( !(dwRequestedInfo & MI_PASSWORD), "Can't get the machine's password!" );
  1860. // Build the filter to find the Computer object with this GUID
  1861. ldap_escape_filter_element(pMachineInfo->Guid, BINL_GUID_LENGTH, EscapedGuid, sizeof(EscapedGuid) );
  1862. //
  1863. // Dont' use ';binary' because win2k Active Directory isn't compatible with the
  1864. // binary tag.
  1865. //
  1866. wsprintf( Filter, L"(&(objectClass=computer)(netbootGUID=%ws))", EscapedGuid );
  1867. #if 0 && DBG
  1868. {
  1869. LPGUID GuidPtr = (LPGUID) &pMachineInfo->Guid;
  1870. BinlPrint((DEBUG_MISC, "Client Guid: {%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}\n",
  1871. GuidPtr->Data1, GuidPtr->Data2, GuidPtr->Data3,
  1872. GuidPtr->Data4[0], GuidPtr->Data4[1], GuidPtr->Data4[2], GuidPtr->Data4[3],
  1873. GuidPtr->Data4[4], GuidPtr->Data4[5], GuidPtr->Data4[6], GuidPtr->Data4[7] ));
  1874. }
  1875. #endif
  1876. RetryConnection:
  1877. dwErr = InitializeConnection( fGlobalSearch, &LdapHandle, &Base );
  1878. if ( dwErr != ERROR_SUCCESS ) {
  1879. BinlPrint((DEBUG_ERRORS,
  1880. "InitializeConnection failed, ec = %x\n",dwErr));
  1881. SetLastError( dwErr );
  1882. dwErr = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
  1883. goto e0;
  1884. }
  1885. LdapError = ldap_search_ext_s(LdapHandle,
  1886. *Base,
  1887. LDAP_SCOPE_SUBTREE,
  1888. Filter,
  1889. ComputerAttrs,
  1890. FALSE,
  1891. NULL,
  1892. NULL,
  1893. &BinlLdapSearchTimeout,
  1894. 0,
  1895. &LdapMessage);
  1896. if ( LdapError != LDAP_SUCCESS ) {
  1897. HandleLdapFailure( LdapError,
  1898. EVENT_WARNING_LDAP_SEARCH_ERROR,
  1899. fGlobalSearch,
  1900. &LdapHandle,
  1901. FALSE ); // don't have lock
  1902. if (LdapHandle == NULL) {
  1903. if (++ldapRetryLimit < LDAP_SERVER_DOWN_LIMIT) {
  1904. goto RetryConnection;
  1905. }
  1906. dwErr = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
  1907. SetLastError( dwErr );
  1908. goto e0;
  1909. }
  1910. BinlPrint((DEBUG_MISC,
  1911. "ldap_search_ext_s %ws failed, ec = %x\n",
  1912. Filter,
  1913. LdapError));
  1914. }
  1915. // Did we get a Computer Object?
  1916. entryCount = ldap_count_entries( LdapHandle, LdapMessage );
  1917. if ( entryCount == 0 ) {
  1918. BinlPrint((DEBUG_MISC,
  1919. "ldap_count_entries %ws returned 0 entries\n",
  1920. Filter ));
  1921. dwErr = ERROR_BINL_INVALID_BINL_CLIENT;
  1922. goto e1; // nope
  1923. }
  1924. // if we get more than more entry back, we will use only the
  1925. // first one.
  1926. CurrentEntry = ldap_first_entry( LdapHandle, LdapMessage );
  1927. if (entryCount > 1) {
  1928. BinlLogDuplicateDsRecords( (LPGUID)&pMachineInfo->Guid, LdapHandle, LdapMessage, CurrentEntry );
  1929. }
  1930. FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"distinguishedName");
  1931. if ( FilePath ) {
  1932. if ( pMachineInfo->dwFlags & MI_MACHINEDN_ALLOC ) {
  1933. BinlFreeMemory( pMachineInfo->MachineDN );
  1934. pMachineInfo->dwFlags &= ~MI_MACHINEDN_ALLOC;
  1935. }
  1936. pMachineInfo->MachineDN = BinlStrDup( *FilePath );
  1937. if ( pMachineInfo->MachineDN ) {
  1938. pMachineInfo->dwFlags |= MI_MACHINEDN | MI_MACHINEDN_ALLOC;
  1939. }
  1940. BinlPrint(( DEBUG_MISC, "MachineDN = %ws\n", pMachineInfo->MachineDN ));
  1941. ldap_value_free( FilePath );
  1942. } else {
  1943. BinlPrint((DEBUG_MISC,
  1944. "couldn't get distinguishedName for %ws\n",
  1945. Filter ));
  1946. }
  1947. FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"netbootInitialization" );
  1948. if ( FilePath ) {
  1949. if ( pMachineInfo->dwFlags & MI_SETUPPATH_ALLOC ) {
  1950. BinlFreeMemory( pMachineInfo->SetupPath );
  1951. pMachineInfo->dwFlags &= ~MI_SETUPPATH_ALLOC;
  1952. }
  1953. pMachineInfo->SetupPath = BinlStrDup( *FilePath );
  1954. if ( pMachineInfo->SetupPath ) {
  1955. pMachineInfo->dwFlags |= MI_SETUPPATH | MI_SETUPPATH_ALLOC;
  1956. BinlPrintDbg(( DEBUG_MISC, "SetupPath = %ws\n", pMachineInfo->SetupPath ));
  1957. }
  1958. ldap_value_free( FilePath );
  1959. }
  1960. FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"netbootMachineFilePath" );
  1961. if ( FilePath ) {
  1962. PWCHAR psz = StrChr( *FilePath, L'\\' );
  1963. if ( psz ) {
  1964. *psz = L'\0'; // terminate
  1965. }
  1966. if (pMachineInfo->dwFlags & MI_HOSTNAME_ALLOC) {
  1967. BinlFreeMemory( pMachineInfo->HostName );
  1968. pMachineInfo->dwFlags &= ~MI_HOSTNAME_ALLOC;
  1969. }
  1970. pMachineInfo->HostName = BinlStrDup( *FilePath );
  1971. if (pMachineInfo->HostName) {
  1972. BinlPrint(( DEBUG_MISC, "HostName = %ws\n", pMachineInfo->HostName ));
  1973. pMachineInfo->dwFlags |= MI_HOSTNAME | MI_HOSTNAME_ALLOC;
  1974. }
  1975. if ( psz ) {
  1976. *psz = L'\\'; // let's put it back to what it started as.
  1977. psz++;
  1978. if (pMachineInfo->dwFlags & MI_BOOTFILENAME_ALLOC) {
  1979. BinlFreeMemory( pMachineInfo->BootFileName );
  1980. pMachineInfo->dwFlags &= ~MI_BOOTFILENAME_ALLOC;
  1981. }
  1982. pMachineInfo->BootFileName = BinlStrDup( psz );
  1983. if ( pMachineInfo->BootFileName ) {
  1984. pMachineInfo->dwFlags |= MI_BOOTFILENAME | MI_BOOTFILENAME_ALLOC;
  1985. BinlPrintDbg(( DEBUG_MISC, "BootFileName = %ws\n", pMachineInfo->BootFileName ));
  1986. }
  1987. }
  1988. ldap_value_free( FilePath );
  1989. }
  1990. FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"netbootSIFFile" );
  1991. if ( FilePath ) {
  1992. if (pMachineInfo->dwFlags & MI_SIFFILENAME_ALLOC) {
  1993. BinlFreeMemory( pMachineInfo->ForcedSifFileName );
  1994. pMachineInfo->dwFlags &= ~MI_SIFFILENAME_ALLOC;
  1995. }
  1996. pMachineInfo->ForcedSifFileName = BinlStrDup( *FilePath );
  1997. if ( pMachineInfo->ForcedSifFileName ) {
  1998. pMachineInfo->dwFlags |= MI_SIFFILENAME_ALLOC;
  1999. BinlPrintDbg(( DEBUG_MISC, "ForcedSifFileName = %ws\n", pMachineInfo->ForcedSifFileName ));
  2000. }
  2001. ldap_value_free( FilePath );
  2002. }
  2003. if ( !(pMachineInfo->dwFlags & MI_HOSTNAME )
  2004. || ( !pMachineInfo->HostName )
  2005. || ( pMachineInfo->HostName[0] == L'\0') ) {
  2006. if ( pMachineInfo->dwFlags & MI_HOSTNAME_ALLOC ) {
  2007. BinlFreeMemory( pMachineInfo->HostName );
  2008. pMachineInfo->dwFlags &= ~MI_HOSTNAME_ALLOC;
  2009. pMachineInfo->HostName = NULL;
  2010. }
  2011. dwErr = BinlGenerateNewEntry( MI_HOSTNAME, SystemArchitecture, &pMachineInfo );
  2012. if ( dwErr != ERROR_SUCCESS ) {
  2013. goto e1;
  2014. }
  2015. }
  2016. if ( !(pMachineInfo->dwFlags & MI_BOOTFILENAME)
  2017. || ( !pMachineInfo->BootFileName )
  2018. || ( pMachineInfo->BootFileName[0] == L'\0') ) {
  2019. if (pMachineInfo->dwFlags & MI_BOOTFILENAME_ALLOC) {
  2020. BinlFreeMemory( pMachineInfo->BootFileName );
  2021. pMachineInfo->BootFileName = NULL;
  2022. pMachineInfo->dwFlags &= ~MI_BOOTFILENAME_ALLOC;
  2023. }
  2024. dwErr = BinlGenerateNewEntry( MI_BOOTFILENAME, SystemArchitecture, &pMachineInfo );
  2025. if ( dwErr != ERROR_SUCCESS ) {
  2026. goto e1;
  2027. }
  2028. }
  2029. FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"sAMAccountName" );
  2030. if ( FilePath ) {
  2031. if (pMachineInfo->dwFlags & MI_SAMNAME_ALLOC) {
  2032. BinlFreeMemory( pMachineInfo->SamName );
  2033. pMachineInfo->dwFlags &= ~MI_SAMNAME_ALLOC;
  2034. }
  2035. pMachineInfo->SamName = BinlStrDup( *FilePath );
  2036. if ( pMachineInfo->SamName ) {
  2037. pMachineInfo->dwFlags |= MI_SAMNAME | MI_SAMNAME_ALLOC;
  2038. BinlPrint(( DEBUG_MISC, "SamName = %ws\n", pMachineInfo->SamName ));
  2039. }
  2040. //
  2041. // For now, the pMachineInfo Name and SamName are the same values,
  2042. // therefore we won't look them up twice in the ldap message.
  2043. //
  2044. #if 0
  2045. ldap_value_free( FilePath );
  2046. }
  2047. FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"sAMAccountName" );
  2048. if ( FilePath ) {
  2049. #endif
  2050. if ( pMachineInfo->dwFlags & MI_NAME_ALLOC ) {
  2051. BinlFreeMemory( pMachineInfo->Name );
  2052. pMachineInfo->dwFlags &= ~MI_NAME_ALLOC;
  2053. }
  2054. pMachineInfo->Name = BinlStrDup( *FilePath );
  2055. if ( pMachineInfo->Name ) {
  2056. if( pMachineInfo->Name[ wcslen(pMachineInfo->Name) - 1 ] == L'$' ) {
  2057. pMachineInfo->Name[ wcslen(pMachineInfo->Name) - 1 ] = L'\0'; // remove '$'
  2058. }
  2059. pMachineInfo->dwFlags |= MI_NAME | MI_NAME_ALLOC;
  2060. BinlPrint(( DEBUG_MISC, "Name = %ws\n", pMachineInfo->Name ));
  2061. }
  2062. ldap_value_free( FilePath );
  2063. }
  2064. FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"dnsHostName" );
  2065. if ( FilePath ) {
  2066. BOOL fEndofString = FALSE;
  2067. PWCHAR psz = *FilePath;
  2068. // skip host name, we get that from the samName
  2069. while ( *psz && *psz!=L'.' )
  2070. psz++;
  2071. if ( !(*psz) ) {
  2072. fEndofString = TRUE;
  2073. }
  2074. *psz = L'\0'; // terminate
  2075. if ( fEndofString == FALSE ) {
  2076. psz++;
  2077. if (pMachineInfo->Domain) {
  2078. BinlFreeMemory( pMachineInfo->Domain );
  2079. }
  2080. pMachineInfo->Domain = BinlStrDup( psz );
  2081. if ( pMachineInfo->Domain )
  2082. {
  2083. pMachineInfo->dwFlags |= MI_DOMAIN;
  2084. BinlPrint(( DEBUG_MISC, "Domain = %ws\n", pMachineInfo->Domain ));
  2085. }
  2086. }
  2087. }
  2088. //
  2089. // track duplicates that we get back
  2090. //
  2091. // first we free all duplicates we have already allocated.
  2092. //
  2093. while (!IsListEmpty(&pMachineInfo->DNsWithSameGuid)) {
  2094. PLIST_ENTRY p = RemoveHeadList(&pMachineInfo->DNsWithSameGuid);
  2095. dupDN = CONTAINING_RECORD(p, DUP_GUID_DN, ListEntry);
  2096. BinlFreeMemory( dupDN );
  2097. }
  2098. while (--entryCount > 0) {
  2099. CurrentEntry = ldap_next_entry( LdapHandle, CurrentEntry );
  2100. if (CurrentEntry == NULL) {
  2101. break;
  2102. }
  2103. FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"dnsHostName" );
  2104. if (!FilePath) {
  2105. FilePath = ldap_get_values( LdapHandle, CurrentEntry, L"sAMAccountName");
  2106. }
  2107. if ( FilePath ) {
  2108. ULONG dupLength, dupLength2;
  2109. BinlPrint(( DEBUG_OSC, "Found duplicate DN in %ws\n", *FilePath ));
  2110. FilePath2 = ldap_get_values( LdapHandle, CurrentEntry, L"distinguishedName");
  2111. dupLength = lstrlenW( *FilePath ) + 1;
  2112. if (FilePath2) {
  2113. dupLength2 = lstrlenW( *FilePath2 ) + 1;
  2114. } else {
  2115. dupLength2 = 1;
  2116. }
  2117. dupDN = BinlAllocateMemory( FIELD_OFFSET(DUP_GUID_DN, DuplicateName[0]) +
  2118. ( (dupLength + dupLength2) * sizeof(WCHAR) ) );
  2119. if ( dupDN ) {
  2120. dupDN->DuplicateDNOffset = dupLength;
  2121. lstrcpyW( &dupDN->DuplicateName[0], *FilePath );
  2122. if (FilePath2) {
  2123. lstrcpyW( &dupDN->DuplicateName[dupLength], *FilePath2 );
  2124. } else {
  2125. dupDN->DuplicateName[dupLength] = L'\0';
  2126. }
  2127. //
  2128. // if the last character is a $, then slam in a NULL to end it.
  2129. //
  2130. if (( dupLength > 1 ) &&
  2131. ( dupDN->DuplicateName[dupLength-2] == L'$' )) {
  2132. dupDN->DuplicateName[dupLength-2] = L'\0';
  2133. }
  2134. InsertTailList( &pMachineInfo->DNsWithSameGuid, &dupDN->ListEntry );
  2135. }
  2136. ldap_value_free( FilePath );
  2137. if (FilePath2) {
  2138. ldap_value_free( FilePath2 );
  2139. }
  2140. }
  2141. }
  2142. e1:
  2143. ldap_msgfree( LdapMessage );
  2144. e0:
  2145. return dwErr;
  2146. }
  2147. DWORD
  2148. InitializeConnection(
  2149. BOOL Global,
  2150. PLDAP * LdapHandle,
  2151. PWCHAR ** Base )
  2152. /*++
  2153. Routine Description:
  2154. Initialize the ldap connection for operating on either the domain or the
  2155. global catalog.
  2156. Arguments:
  2157. Global - TRUE if GC should be used
  2158. LdapHandle - Returns the handle for further operations
  2159. OperationalAttributeLdapMessage - Returns message containing Base so that it can be freed later
  2160. Base - DN of where to start searches for computer objects.
  2161. Return Value:
  2162. ldap error
  2163. --*/
  2164. {
  2165. PLDAPMessage OperationalAttributeLdapMessage;
  2166. PWCHAR Attrs[2];
  2167. PLDAPMessage CurrentEntry;
  2168. PWCHAR *LdapValue;
  2169. DWORD LdapError = ERROR_SUCCESS;
  2170. PLDAP *LdapHandleCurrent;
  2171. PWCHAR ** LdapBaseCurrent;
  2172. ULONG temp;
  2173. TraceFunc( "InitializeConnection( )\n" );
  2174. // Use critical section to avoid two threads initialising the same parameters
  2175. EnterCriticalSection(&gcsDHCPBINL);
  2176. if ( !Global ) {
  2177. LdapHandleCurrent = &DCLdapHandle;
  2178. LdapBaseCurrent = &DCBase;
  2179. } else {
  2180. LdapHandleCurrent = &GCLdapHandle;
  2181. LdapBaseCurrent = &GCBase;
  2182. }
  2183. if ( !(*LdapHandleCurrent) ) {
  2184. if (Global) {
  2185. *LdapHandleCurrent = ldap_initW( BinlGlobalDefaultGC, LDAP_GC_PORT);
  2186. temp = DS_DIRECTORY_SERVICE_REQUIRED |
  2187. DS_IP_REQUIRED |
  2188. DS_GC_SERVER_REQUIRED;
  2189. } else {
  2190. *LdapHandleCurrent = ldap_init( BinlGlobalDefaultDS, LDAP_PORT);
  2191. temp = DS_DIRECTORY_SERVICE_REQUIRED |
  2192. DS_IP_REQUIRED;
  2193. }
  2194. if (!*LdapHandleCurrent) {
  2195. BinlPrint(( DEBUG_ERRORS, "Failed to initialize LDAP connection.\n" ));
  2196. LdapError = LDAP_CONNECT_ERROR;
  2197. LogLdapError( (Global ? EVENT_WARNING_LDAP_INIT_ERROR_GC :
  2198. EVENT_WARNING_LDAP_INIT_ERROR_DC),
  2199. GetLastError(),
  2200. NULL
  2201. );
  2202. goto e0;
  2203. }
  2204. ldap_set_option(*LdapHandleCurrent, LDAP_OPT_GETDSNAME_FLAGS, &temp );
  2205. if (Global == FALSE) {
  2206. temp = BinlLdapOptReferrals;
  2207. ldap_set_option(*LdapHandleCurrent, LDAP_OPT_REFERRALS, (void *) &temp );
  2208. } else {
  2209. //
  2210. // At some future time, the GC is going to return referrals to
  2211. // authoritative DCs when the GC doesn't contain all the
  2212. // attributes. We'll enable referrals so that it "just works".
  2213. //
  2214. temp = (ULONG)((ULONG_PTR)LDAP_OPT_ON);
  2215. ldap_set_option(*LdapHandleCurrent, LDAP_OPT_REFERRALS, (void *) &temp );
  2216. }
  2217. temp = LDAP_VERSION3;
  2218. ldap_set_option(*LdapHandleCurrent, LDAP_OPT_VERSION, &temp );
  2219. LdapError = ldap_connect(*LdapHandleCurrent,0);
  2220. if (LdapError != LDAP_SUCCESS) {
  2221. LogLdapError( (Global ? EVENT_WARNING_LDAP_INIT_ERROR_GC :
  2222. EVENT_WARNING_LDAP_INIT_ERROR_DC),
  2223. LdapError,
  2224. *LdapHandleCurrent
  2225. );
  2226. BinlPrint(( DEBUG_ERRORS, "ldap_connect failed: %lx\n", LdapError ));
  2227. goto e1;
  2228. }
  2229. LdapError = ldap_bind_s(*LdapHandleCurrent, NULL, NULL, LDAP_AUTH_SSPI);
  2230. if (LdapError != LDAP_SUCCESS) {
  2231. BinlPrint(( DEBUG_ERRORS, "ldap_bind_s failed: %lx\n", LdapError ));
  2232. LogLdapError( EVENT_WARNING_LDAP_BIND_ERROR,
  2233. LdapError,
  2234. *LdapHandleCurrent
  2235. );
  2236. goto e1;
  2237. }
  2238. }
  2239. //
  2240. // Connected to Directory Service. Find out where in the DS we
  2241. // should start looking for the computer.
  2242. //
  2243. if ( !(*LdapBaseCurrent) )
  2244. {
  2245. DWORD count;
  2246. Attrs[0] = &L"defaultNamingContext";
  2247. Attrs[1] = NULL;
  2248. LdapError = ldap_search_ext_s(*LdapHandleCurrent,
  2249. NULL, // base
  2250. LDAP_SCOPE_BASE,
  2251. L"objectClass=*",// filter
  2252. Attrs,
  2253. FALSE,
  2254. NULL,
  2255. NULL,
  2256. &BinlLdapSearchTimeout,
  2257. 0,
  2258. &OperationalAttributeLdapMessage);
  2259. if ( LdapError != LDAP_SUCCESS ) {
  2260. BinlPrint(( DEBUG_ERRORS, "ldap_search_ext_s failed: %x\n", LdapError ));
  2261. HandleLdapFailure( LdapError,
  2262. EVENT_WARNING_LDAP_SEARCH_ERROR,
  2263. Global,
  2264. LdapHandleCurrent,
  2265. TRUE ); // we have lock
  2266. if (*LdapHandleCurrent == NULL) {
  2267. goto e1;
  2268. }
  2269. }
  2270. count = ldap_count_entries( *LdapHandleCurrent, OperationalAttributeLdapMessage );
  2271. if ( count == 0 ) {
  2272. BinlPrint(( DEBUG_ERRORS, "Failed to find the defaultNamingContext.\n" ));
  2273. LdapError = LDAP_NO_RESULTS_RETURNED;
  2274. LogLdapError( EVENT_WARNING_LDAP_SEARCH_ERROR,
  2275. LdapError,
  2276. *LdapHandleCurrent
  2277. );
  2278. goto e0;
  2279. }
  2280. //
  2281. // the DS should always only return us a single root DSE record.
  2282. // It would be completely broken if it returned more than one.
  2283. //
  2284. BinlAssert( count == 1 );
  2285. CurrentEntry = ldap_first_entry( *LdapHandleCurrent, OperationalAttributeLdapMessage );
  2286. LdapValue = ldap_get_values( *LdapHandleCurrent, CurrentEntry, Attrs[0] );
  2287. if (LdapValue == NULL) {
  2288. BinlPrint(( DEBUG_ERRORS, "Failed to find the defaultNamingContext.\n" ));
  2289. LdapError = LDAP_NO_RESULTS_RETURNED;
  2290. goto e2;
  2291. }
  2292. *LdapBaseCurrent = LdapValue;
  2293. e2:
  2294. ldap_msgfree( OperationalAttributeLdapMessage );
  2295. }
  2296. e0:
  2297. if ( LdapHandle ) {
  2298. *LdapHandle = *LdapHandleCurrent;
  2299. }
  2300. if ( Base ) {
  2301. *Base = *LdapBaseCurrent;
  2302. }
  2303. LeaveCriticalSection(&gcsDHCPBINL);
  2304. return LdapError;
  2305. e1:
  2306. BinlPrint(( DEBUG_ERRORS, "Failed to connect to LDAP server.\n" ));
  2307. if (*LdapHandleCurrent != NULL) {
  2308. ldap_unbind(*LdapHandleCurrent);
  2309. *LdapHandleCurrent = NULL;
  2310. }
  2311. goto e0;
  2312. }
  2313. VOID
  2314. HandleLdapFailure(
  2315. DWORD LdapError,
  2316. DWORD EventId,
  2317. BOOL GlobalCatalog,
  2318. PLDAP *LdapHandle,
  2319. BOOL HaveLock
  2320. )
  2321. //
  2322. // This routine is called when one of our global handles (not per user handle)
  2323. // comes back with a serious error. We reset it to try again later.
  2324. //
  2325. {
  2326. if ((LdapError == LDAP_UNAVAILABLE) ||
  2327. (LdapError == LDAP_SERVER_DOWN) ||
  2328. (LdapError == LDAP_CONNECT_ERROR) ||
  2329. (LdapError == LDAP_TIMEOUT)) {
  2330. PLDAP *LdapHandleCurrent;
  2331. PWCHAR ** LdapBaseCurrent;
  2332. if (!HaveLock) {
  2333. EnterCriticalSection(&gcsDHCPBINL);
  2334. }
  2335. LdapHandleCurrent = GlobalCatalog ? &GCLdapHandle : &DCLdapHandle;
  2336. LdapBaseCurrent = GlobalCatalog ? &GCBase : &DCBase;
  2337. if (EventId) {
  2338. LogLdapError( EventId,
  2339. LdapError,
  2340. (LdapHandle != NULL ? *LdapHandle : *LdapHandleCurrent)
  2341. );
  2342. }
  2343. if (LdapHandle) {
  2344. ASSERT( *LdapHandle == *LdapHandleCurrent );
  2345. *LdapHandle = NULL;
  2346. }
  2347. FreeConnection( LdapHandleCurrent, LdapBaseCurrent );
  2348. if (!HaveLock) {
  2349. LeaveCriticalSection(&gcsDHCPBINL);
  2350. }
  2351. }
  2352. return;
  2353. }
  2354. VOID
  2355. FreeConnection(
  2356. PLDAP * LdapHandle,
  2357. PWCHAR ** Base)
  2358. /*++
  2359. Routine Description:
  2360. Free the ldap connection for operating on either the domain or the
  2361. global catalog.
  2362. Arguments:
  2363. LdapHandle - The handle for further operations
  2364. Base - DN of where to start searches for computer objects to be freed.
  2365. Return Value:
  2366. None.
  2367. --*/
  2368. {
  2369. TraceFunc( "FreeConnection( )\n" );
  2370. if (*LdapHandle) {
  2371. ldap_unbind( *LdapHandle );
  2372. *LdapHandle = NULL;
  2373. }
  2374. if (*Base) {
  2375. ldap_value_free(*Base);
  2376. *Base = NULL;
  2377. }
  2378. }
  2379. VOID
  2380. FreeConnections
  2381. (
  2382. VOID
  2383. )
  2384. /*++
  2385. Routine Description:
  2386. Terminate any LDAP requests because we are stopping immediately. We
  2387. wait until all threads are stopped because the threads may have pointers
  2388. to the values we're going to free.
  2389. Arguments:
  2390. None.
  2391. Return Value:
  2392. None.
  2393. --*/
  2394. {
  2395. //
  2396. // clear out the cache, wait until all are marked as not being
  2397. // processed. We do this because the threads have pointers to DCBase,
  2398. // GCBase, etc and if we just blow them away, they may AV.
  2399. //
  2400. BinlCloseCache();
  2401. TraceFunc( "FreeConnections( )\n" );
  2402. FreeConnection( &DCLdapHandle, &DCBase);
  2403. FreeConnection( &GCLdapHandle, &GCBase);
  2404. }
  2405. DWORD
  2406. FindSCPForBinlServer(
  2407. PWCHAR * ResultPath,
  2408. PWCHAR * MachinePath,
  2409. BOOL GlobalSearch)
  2410. /*++
  2411. Routine Description:
  2412. Use the Directory Service to lookup the settings for this service.
  2413. Arguments:
  2414. GlobalSearch - TRUE if GC should be used
  2415. Return Value:
  2416. ERROR_SUCCESS or BINL_CANT_FIND_SERVER_MAO or ERROR_OUTOFMEMORY
  2417. --*/
  2418. {
  2419. DWORD Error;
  2420. PLDAP LdapHandle;
  2421. DWORD LdapError;
  2422. DWORD count;
  2423. ULONG ldapRetryLimit = 0;
  2424. PWCHAR * DsPath;
  2425. PLDAPMessage CurrentEntry;
  2426. PLDAPMessage LdapMessage = NULL;
  2427. PWCHAR ServerDN = NULL;
  2428. BOOL retryDN = TRUE;
  2429. // Paramters we want from the Computer Object
  2430. PWCHAR ComputerAttrs[2];
  2431. ComputerAttrs[0] = &L"netbootSCPBL";
  2432. ComputerAttrs[1] = NULL;
  2433. TraceFunc( "FindSCPForBinlServer( )\n" );
  2434. RetryGetDN:
  2435. EnterCriticalSection( &gcsParameters );
  2436. if (BinlGlobalOurFQDNName == NULL) {
  2437. LeaveCriticalSection( &gcsParameters );
  2438. Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
  2439. BinlPrintDbg((DEBUG_ERRORS, "!! Error 0x%08x we don't have a FQDN for ourselves.\n", Error ));
  2440. goto e0;
  2441. }
  2442. ServerDN = (PWCHAR) BinlAllocateMemory( (wcslen( BinlGlobalOurFQDNName ) + 1) * sizeof(WCHAR) );
  2443. if ( !ServerDN ) {
  2444. LeaveCriticalSection( &gcsParameters );
  2445. Error = E_OUTOFMEMORY;
  2446. goto e0;
  2447. }
  2448. // It should be something like this:
  2449. // ServerDN = "cn=server,cn=computers,dc=microsoft,dc=com"
  2450. lstrcpyW( ServerDN, BinlGlobalOurFQDNName );
  2451. LeaveCriticalSection( &gcsParameters );
  2452. Error = InitializeConnection(GlobalSearch, &LdapHandle, NULL);
  2453. if ( Error != ERROR_SUCCESS ) {
  2454. BinlPrintDbg(( DEBUG_ERRORS, "!!Error 0x%08x - Ldap Connection Failed.\n", Error ));
  2455. SetLastError( Error );
  2456. Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
  2457. goto e0;
  2458. }
  2459. RetrySearch:
  2460. LdapError = ldap_search_ext_s(LdapHandle,
  2461. ServerDN,
  2462. LDAP_SCOPE_BASE,
  2463. L"objectClass=*",
  2464. ComputerAttrs,
  2465. FALSE,
  2466. NULL,
  2467. NULL,
  2468. &BinlLdapSearchTimeout,
  2469. 0,
  2470. &LdapMessage);
  2471. //
  2472. // if the object isn't found, then something is amiss.. go grab the DN
  2473. // again.
  2474. //
  2475. if ((LdapError == LDAP_NO_SUCH_OBJECT) && retryDN) {
  2476. retryDN = FALSE;
  2477. // if we didn't find an entry or it was busy, retry
  2478. (VOID) GetOurServerInfo();
  2479. BinlFreeMemory( ServerDN );
  2480. ServerDN = NULL;
  2481. goto RetryGetDN;
  2482. }
  2483. if (((LdapError == LDAP_BUSY) || (LdapError == LDAP_NO_SUCH_OBJECT)) &&
  2484. (++ldapRetryLimit < LDAP_BUSY_LIMIT)) {
  2485. Sleep( LDAP_BUSY_DELAY );
  2486. goto RetrySearch;
  2487. }
  2488. count = ldap_count_entries( LdapHandle, LdapMessage );
  2489. if (count == 0) {
  2490. if (LdapError == LDAP_SUCCESS) {
  2491. LdapError = LDAP_TIMELIMIT_EXCEEDED;
  2492. }
  2493. BinlPrintDbg(( DEBUG_ERRORS, "!!LdapError 0x%08x - LDAP search failed... will retry later.\n", LdapError ));
  2494. BinlReportEventW( EVENT_ERROR_LOCATING_SCP,
  2495. EVENTLOG_ERROR_TYPE,
  2496. 0,
  2497. sizeof(LdapError),
  2498. NULL,
  2499. &LdapError
  2500. );
  2501. Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
  2502. goto e1;
  2503. }
  2504. BinlAssertMsg( count == 1, "Count should have been 1." );
  2505. if ( count != 1 ) {
  2506. BinlPrintDbg(( DEBUG_ERRORS, "!!Error - LDAP search returned more than one SCP record for us.\n" ));
  2507. BinlReportEventW( BINL_DUPLICATE_MAO_RECORD,
  2508. EVENTLOG_ERROR_TYPE,
  2509. 0,
  2510. sizeof(count),
  2511. NULL,
  2512. &count
  2513. );
  2514. Error = ERROR_BINL_CANT_FIND_SERVER_MAO;
  2515. goto e1;
  2516. }
  2517. //
  2518. // Get the SCP
  2519. //
  2520. CurrentEntry = ldap_first_entry( LdapHandle, LdapMessage );
  2521. DsPath = ldap_get_values( LdapHandle, CurrentEntry, L"netbootSCPBL" );
  2522. if ( !DsPath ) {
  2523. BinlPrintDbg(( DEBUG_ERRORS, "!!Error - Could not get 'netbootSCPBL' from the server's MAO\n" ))
  2524. Error = ERROR_BINL_CANT_FIND_SERVER_MAO;
  2525. goto e1;
  2526. }
  2527. *ResultPath = (PWCHAR) BinlAllocateMemory( (wcslen(*DsPath) + 1) * sizeof(WCHAR) );
  2528. if ( *ResultPath == NULL ) {
  2529. BinlPrintDbg(( DEBUG_ERRORS, "!!Error - Out of memory.\n" ));
  2530. Error = ERROR_OUTOFMEMORY;
  2531. goto e2;
  2532. }
  2533. wcscpy( *ResultPath, *DsPath );
  2534. *MachinePath = ServerDN;
  2535. ServerDN = NULL; // prevent freeing
  2536. Error = ERROR_SUCCESS;
  2537. e2:
  2538. ldap_value_free(DsPath);
  2539. e1:
  2540. ldap_msgfree( LdapMessage );
  2541. e0:
  2542. if ( ServerDN )
  2543. BinlFreeMemory( ServerDN );
  2544. return Error;
  2545. }
  2546. DWORD
  2547. UpdateSettingsUsingResults(
  2548. PLDAP LdapHandle,
  2549. PLDAPMessage LdapMessage,
  2550. LPWSTR ComputerAttrs[],
  2551. PDWORD NumberOfAttributesFound OPTIONAL
  2552. )
  2553. {
  2554. PLDAPMessage CurrentEntry;
  2555. DWORD LdapError = LDAP_SUCCESS;
  2556. DWORD count;
  2557. DWORD countFound = 0;
  2558. TraceFunc( "UpdateSettingsUsingResults( ... )\n" );
  2559. CurrentEntry = ldap_first_entry( LdapHandle, LdapMessage );
  2560. for ( count = 0; ComputerAttrs[count] != NULL; count++ ) {
  2561. PWCHAR * Attribute;
  2562. Attribute = ldap_get_values( LdapHandle, CurrentEntry, ComputerAttrs[count] );
  2563. if (Attribute == NULL) {
  2564. #if DBG
  2565. CHAR Temp[MAX_PATH];
  2566. wcstombs( Temp, ComputerAttrs[count], wcslen(ComputerAttrs[count]) + 1 );
  2567. BinlPrintDbg(( DEBUG_OPTIONS, "Did not find attribute '%s'... skipping\n", Temp ));
  2568. #endif
  2569. if (count != 1) { // NewMachineOU
  2570. continue; // skip and use default
  2571. }
  2572. } else {
  2573. //
  2574. // Increment the count of attributes found.
  2575. //
  2576. countFound++;
  2577. }
  2578. switch( count )
  2579. {
  2580. case 0: // NewMachineNamingPolicy
  2581. {
  2582. DWORD Length = wcslen( *Attribute ) + 1;
  2583. PWCHAR psz = (PWCHAR) BinlAllocateMemory( Length * sizeof(WCHAR) );
  2584. BinlAssert( StrCmp( ComputerAttrs[0], L"netbootNewMachineNamingPolicy" ) == 0 );
  2585. if ( psz )
  2586. {
  2587. wcscpy( psz, *Attribute );
  2588. EnterCriticalSection(&gcsParameters);
  2589. if ( NewMachineNamingPolicy != NULL )
  2590. {
  2591. BinlFreeMemory( NewMachineNamingPolicy );
  2592. }
  2593. NewMachineNamingPolicy = psz;
  2594. LeaveCriticalSection(&gcsParameters);
  2595. }
  2596. BinlPrint(( DEBUG_OPTIONS, "NewMachineNamingPolicy = '%ws'\n", NewMachineNamingPolicy ));
  2597. }
  2598. break;
  2599. case 1: // NewMachineOU
  2600. {
  2601. LPWSTR psz;
  2602. DWORD Length;
  2603. BOOL getServerInfo;
  2604. BinlAssert( StrCmp( ComputerAttrs[1], L"netbootNewMachineOU" ) == 0 );
  2605. if (Attribute == NULL || *Attribute == NULL) {
  2606. Length = 1;
  2607. } else {
  2608. Length = wcslen( *Attribute ) + 1;
  2609. }
  2610. psz = (LPWSTR) BinlAllocateMemory( Length * sizeof(WCHAR) );
  2611. if (psz == NULL) {
  2612. LdapError = LDAP_NO_MEMORY;
  2613. break;
  2614. }
  2615. if (Length == 1) {
  2616. *psz = L'\0';
  2617. } else {
  2618. wcscpy( psz, *Attribute );
  2619. }
  2620. EnterCriticalSection(&gcsParameters);
  2621. getServerInfo = (BOOL)( (BinlGlobalDefaultContainer == NULL) ||
  2622. (StrCmp(BinlGlobalDefaultContainer, psz) != 0) );
  2623. if ( BinlGlobalDefaultContainer != NULL )
  2624. {
  2625. BinlFreeMemory( BinlGlobalDefaultContainer );
  2626. }
  2627. BinlGlobalDefaultContainer = psz;
  2628. LeaveCriticalSection(&gcsParameters);
  2629. if ( getServerInfo ) {
  2630. ULONG Error = GetOurServerInfo();
  2631. if (Error != ERROR_SUCCESS) {
  2632. BinlPrintDbg(( DEBUG_ERRORS, "GetOurServerInfo returned 0x%x, we had a new default container.\n", Error ));
  2633. }
  2634. }
  2635. BinlPrint(( DEBUG_OPTIONS, "DefaultContainer = %ws\n", BinlGlobalDefaultContainer ));
  2636. }
  2637. break;
  2638. case 2: // MaxClients
  2639. {
  2640. CHAR Temp[10];
  2641. BinlAssert( StrCmp( ComputerAttrs[2], L"netbootMaxClients" ) == 0 );
  2642. wcstombs( Temp, *Attribute, wcslen( *Attribute ) + 1 );
  2643. BinlMaxClients = atoi( Temp );
  2644. BinlPrint(( DEBUG_OPTIONS, "BinlMaxClients = %u\n", BinlMaxClients ));
  2645. }
  2646. break;
  2647. case 3: // CurrentClientCount
  2648. {
  2649. CHAR Temp[10];
  2650. BinlAssert( StrCmp( ComputerAttrs[3], L"netbootCurrentClientCount" ) == 0 );
  2651. wcstombs( Temp, *Attribute, wcslen( *Attribute ) + 1 );
  2652. CurrentClientCount = atoi( Temp );
  2653. BinlPrint(( DEBUG_OPTIONS, "(Last) CurrentClientCount = %u\n", CurrentClientCount ));
  2654. }
  2655. break;
  2656. case 4: // AnswerRequest
  2657. BinlAssert( StrCmp ( ComputerAttrs[4], L"netbootAnswerRequests" ) == 0 );
  2658. if ( wcscmp( *Attribute, L"TRUE" ) == 0 )
  2659. {
  2660. AnswerRequests = TRUE;
  2661. }
  2662. else
  2663. {
  2664. AnswerRequests = FALSE;
  2665. }
  2666. BinlPrint(( DEBUG_OPTIONS, "AnswerRequests = %s\n", BOOLTOSTRING( AnswerRequests ) ));
  2667. break;
  2668. case 5: // AnswerOnlyValidClients
  2669. BinlAssert( StrCmp( ComputerAttrs[5], L"netbootAnswerOnlyValidClients" ) == 0 );
  2670. if ( wcscmp( *Attribute, L"TRUE" ) == 0 ) {
  2671. AnswerOnlyValidClients = TRUE;
  2672. } else {
  2673. AnswerOnlyValidClients = FALSE;
  2674. }
  2675. BinlPrint(( DEBUG_OPTIONS, "AnswerOnlyValidClients = %s\n", BOOLTOSTRING( AnswerOnlyValidClients ) ));
  2676. break;
  2677. case 6: // AllowNewClients
  2678. BinlAssert( StrCmp( ComputerAttrs[6], L"netbootAllowNewClients" ) == 0 );
  2679. if ( wcscmp( *Attribute, L"TRUE" ) == 0 )
  2680. {
  2681. AllowNewClients = TRUE;
  2682. }
  2683. else
  2684. {
  2685. AllowNewClients = FALSE;
  2686. }
  2687. BinlPrint(( DEBUG_OPTIONS, "AllowNewClients = %s\n", BOOLTOSTRING( AllowNewClients ) ));
  2688. break;
  2689. case 7: // LimitClients
  2690. BinlAssert( StrCmp( ComputerAttrs[7], L"netbootLimitClients" ) == 0 );
  2691. if ( wcscmp( *Attribute, L"TRUE" ) == 0 )
  2692. {
  2693. LimitClients = TRUE;
  2694. }
  2695. else
  2696. {
  2697. LimitClients = FALSE;
  2698. }
  2699. BinlPrint(( DEBUG_OPTIONS, "LimitClients = %s\n", BOOLTOSTRING( LimitClients ) ));
  2700. break;
  2701. case 8: // IntellimirrorOSes
  2702. case 9: // Tools
  2703. case 10: // LocalInstallOSes
  2704. BinlAssert( StrCmp( ComputerAttrs[8], L"netbootIntellimirrorOSes" ) == 0 );
  2705. BinlAssert( StrCmp( ComputerAttrs[9], L"netbootTools" ) == 0 );
  2706. BinlAssert( StrCmp( ComputerAttrs[10], L"netbootLocalInstallOSes" ) == 0 );
  2707. //
  2708. // TODO: Tie these in with OS Chooser - this is still TBD.
  2709. //
  2710. break;
  2711. default:
  2712. // Somethings wrong
  2713. BinlAssert( 0 );
  2714. }
  2715. if (Attribute != NULL) {
  2716. ldap_value_free(Attribute);
  2717. }
  2718. }
  2719. if ( ARGUMENT_PRESENT(NumberOfAttributesFound) ) {
  2720. *NumberOfAttributesFound = countFound;
  2721. }
  2722. return LdapError;
  2723. }
  2724. DWORD
  2725. GetBinlServerParameters(
  2726. BOOL GlobalSearch)
  2727. /*++
  2728. Routine Description:
  2729. Use the Directory Service to lookup the settings for this service.
  2730. Arguments:
  2731. GlobalSearch - TRUE if GC should be used
  2732. Return Value:
  2733. ERROR_SUCCESS or BINL_CANT_FIND_SERVER_MAO
  2734. --*/
  2735. {
  2736. DWORD Error;
  2737. PLDAP LdapHandle;
  2738. DWORD LdapError;
  2739. DWORD count;
  2740. ULONG ldapRetryLimit = 0;
  2741. PLDAPMessage LdapMessage;
  2742. // Paramters we want from the IntelliMirror-SCP
  2743. // NOTE: These must be the same ordinals as those used in
  2744. // UpdateSettingsUsingResults( ).
  2745. PWCHAR ComputerAttrs[12];
  2746. ComputerAttrs[0] = &L"netbootNewMachineNamingPolicy";
  2747. ComputerAttrs[1] = &L"netbootNewMachineOU";
  2748. ComputerAttrs[2] = &L"netbootMaxClients";
  2749. ComputerAttrs[3] = &L"netbootCurrentClientCount";
  2750. ComputerAttrs[4] = &L"netbootAnswerRequests";
  2751. ComputerAttrs[5] = &L"netbootAnswerOnlyValidClients";
  2752. ComputerAttrs[6] = &L"netbootAllowNewClients";
  2753. ComputerAttrs[7] = &L"netbootLimitClients";
  2754. ComputerAttrs[8] = &L"netbootIntellimirrorOSes";
  2755. ComputerAttrs[9] = &L"netbootTools";
  2756. ComputerAttrs[10] = &L"netbootLocalInstallOSes";
  2757. ComputerAttrs[11] = NULL;
  2758. TraceFunc( "GetBinlServerParameters( )\n" );
  2759. Error = FindSCPForBinlServer( &BinlGlobalSCPPath, &BinlGlobalServerDN, GlobalSearch );
  2760. if ( Error != ERROR_SUCCESS ) {
  2761. BinlPrint(( DEBUG_ERRORS, "!!Error 0x%08x - SCP not found. Default settings being used.\n", Error ));
  2762. goto e0;
  2763. }
  2764. BinlPrint(( DEBUG_OPTIONS, "ServerDN = '%ws'\n", BinlGlobalServerDN ));
  2765. BinlPrint(( DEBUG_OPTIONS, "SCPDN = '%ws'\n", BinlGlobalSCPPath ));
  2766. RetryConnection:
  2767. Error = InitializeConnection( GlobalSearch, &LdapHandle, NULL );
  2768. if ( Error != ERROR_SUCCESS ) {
  2769. SetLastError( Error );
  2770. Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
  2771. goto e0;
  2772. }
  2773. Retry:
  2774. LdapError = ldap_search_ext_s(LdapHandle,
  2775. BinlGlobalSCPPath,
  2776. LDAP_SCOPE_BASE,
  2777. L"objectClass=*",
  2778. ComputerAttrs,
  2779. FALSE,
  2780. NULL,
  2781. NULL,
  2782. NULL,
  2783. 0,
  2784. &LdapMessage);
  2785. if ((LdapError == LDAP_BUSY) && (++ldapRetryLimit < LDAP_BUSY_LIMIT)) {
  2786. Sleep( LDAP_BUSY_DELAY );
  2787. goto Retry;
  2788. }
  2789. if (LdapError != LDAP_SUCCESS) {
  2790. HandleLdapFailure( LdapError,
  2791. EVENT_WARNING_LDAP_SEARCH_ERROR,
  2792. GlobalSearch,
  2793. &LdapHandle,
  2794. FALSE ); // don't have lock
  2795. if (LdapHandle == NULL) {
  2796. if (++ldapRetryLimit < LDAP_SERVER_DOWN_LIMIT) {
  2797. goto RetryConnection;
  2798. }
  2799. goto e0;
  2800. }
  2801. }
  2802. count = ldap_count_entries( LdapHandle, LdapMessage );
  2803. if (count == 0) {
  2804. if (LdapError == LDAP_SUCCESS) {
  2805. LdapError = LDAP_TIMELIMIT_EXCEEDED;
  2806. }
  2807. BinlPrintDbg(( DEBUG_ERRORS, "!!LdapError 0x%08x - Failed to retrieve parameters... will retry later.\n", LdapError ));
  2808. BinlReportEventW( EVENT_ERROR_LOCATING_SCP,
  2809. EVENTLOG_ERROR_TYPE,
  2810. 0,
  2811. sizeof(LdapError),
  2812. NULL,
  2813. &LdapError
  2814. );
  2815. Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
  2816. goto e1;
  2817. }
  2818. BinlAssertMsg( count == 1, "Count should have been one. Is the SCP missing?" );
  2819. // We did a base level search, we better only have gotten one record back.
  2820. BinlAssert( count == 1 );
  2821. // Retrieve the results into the settings
  2822. LdapError = UpdateSettingsUsingResults( LdapHandle, LdapMessage, ComputerAttrs, &count );
  2823. if ( LdapError == LDAP_SUCCESS )
  2824. {
  2825. BinlReportEventW( count != 0 ? EVENT_SCP_READ_SUCCESSFULLY :
  2826. EVENT_SCP_READ_SUCCESSFULLY_EMPTY,
  2827. count != 0 ? EVENTLOG_INFORMATION_TYPE :
  2828. EVENTLOG_WARNING_TYPE,
  2829. 0,
  2830. 0,
  2831. NULL,
  2832. NULL
  2833. );
  2834. }
  2835. e1:
  2836. ldap_msgfree( LdapMessage );
  2837. e0:
  2838. return Error;
  2839. }
  2840. VOID
  2841. BinlLogDuplicateDsRecords (
  2842. LPGUID Guid,
  2843. LDAP *LdapHandle,
  2844. LDAPMessage *LdapMessage,
  2845. LDAPMessage *CurrentEntry
  2846. )
  2847. //
  2848. // Log an error that we've received duplicate records for a client when
  2849. // we looked them up by GUID.
  2850. //
  2851. // We log the DNs so that the administrator can look them up.
  2852. //
  2853. {
  2854. LPWSTR strings[4];
  2855. LPWSTR dn1;
  2856. LPWSTR dn2;
  2857. ULONG strCount = 0; // up to two strings to log
  2858. PLDAPMessage nextEntry = ldap_next_entry( LdapHandle, LdapMessage );
  2859. LPWSTR GuidString;
  2860. if (SUCCEEDED(StringFromIID( (REFIID)Guid, &GuidString ))) {
  2861. strCount += 1;
  2862. }
  2863. dn1 = ldap_get_dnW( LdapHandle, CurrentEntry );
  2864. if (nextEntry != NULL) {
  2865. dn2 = ldap_get_dnW( LdapHandle, nextEntry );
  2866. } else {
  2867. dn2 = NULL;
  2868. }
  2869. if (dn2 != NULL) {
  2870. if (dn1 == NULL) {
  2871. dn1 = dn2;
  2872. dn2 = NULL;
  2873. } else {
  2874. strCount += 1;
  2875. }
  2876. }
  2877. if (dn1 != NULL) {
  2878. strCount += 1;
  2879. }
  2880. BinlPrint(( DEBUG_ERRORS, "Warning - BINL received multiple records for a single GUID.\n" ));
  2881. strings[0] = GuidString;
  2882. strings[1] = dn1;
  2883. strings[2] = dn2;
  2884. strings[3] = NULL;
  2885. BinlReportEventW( BINL_DUPLICATE_DS_RECORD,
  2886. EVENTLOG_WARNING_TYPE,
  2887. strCount,
  2888. 0,
  2889. strings,
  2890. NULL
  2891. );
  2892. ldap_memfree( dn1 ); // it's ok to call ldap_memfree with null
  2893. ldap_memfree( dn2 );
  2894. CoTaskMemFree( GuidString );
  2895. }
  2896. #ifndef DSCRACKNAMES_DNS
  2897. DWORD
  2898. BinlDNStoFQDN(
  2899. PWCHAR pMachineDNS,
  2900. PWCHAR * ppMachineDN )
  2901. {
  2902. DWORD Error;
  2903. DWORD LdapError;
  2904. WCHAR FilterTemplate[] = L"dnsHostName=%ws";
  2905. PWCHAR Filter = NULL;
  2906. PWCHAR ComputerAttrs[2];
  2907. PLDAPMessage CurrentEntry;
  2908. PLDAPMessage LdapMessage;
  2909. LDAP *LdapHandle;
  2910. PWCHAR * Base;
  2911. PWCHAR * MachineDN;
  2912. DWORD count;
  2913. DWORD uSize;
  2914. ULONG ldapRetryLimit = 0;
  2915. TraceFunc( "BinlDNStoFQDN( )\n" );
  2916. BinlAssert( ppMachineDN );
  2917. BinlAssert( pMachineDNS );
  2918. ComputerAttrs[0] = &L"distinguishedName";
  2919. ComputerAttrs[1] = NULL;
  2920. // Build the filter to find the Computer object
  2921. uSize = sizeof(FilterTemplate) // include NULL terminater
  2922. + (wcslen( pMachineDNS ) * sizeof(WCHAR));
  2923. Filter = (LPWSTR) BinlAllocateMemory( uSize );
  2924. if ( !Filter ) {
  2925. Error = E_OUTOFMEMORY;
  2926. goto e0;
  2927. }
  2928. wsprintf( Filter, FilterTemplate, pMachineDNS );
  2929. BinlPrintDbg(( DEBUG_MISC, "Searching for %ws...\n", Filter ));
  2930. RetryConnection:
  2931. Error = InitializeConnection( FALSE, &LdapHandle, &Base );
  2932. if ( Error != ERROR_SUCCESS ) {
  2933. SetLastError( Error );
  2934. Error = ERROR_BINL_INITIALIZE_LDAP_CONNECTION_FAILED;
  2935. goto e0;
  2936. }
  2937. Retry:
  2938. LdapError = ldap_search_ext_s( LdapHandle,
  2939. *Base,
  2940. LDAP_SCOPE_SUBTREE,
  2941. Filter,
  2942. ComputerAttrs,
  2943. FALSE,
  2944. NULL,
  2945. NULL,
  2946. NULL,
  2947. 0,
  2948. &LdapMessage);
  2949. switch (LdapError)
  2950. {
  2951. case LDAP_SUCCESS:
  2952. break;
  2953. case LDAP_BUSY:
  2954. if (++ldapRetryLimit < LDAP_BUSY_LIMIT) {
  2955. Sleep( LDAP_BUSY_DELAY );
  2956. goto Retry;
  2957. }
  2958. // lack of break is on purpose.
  2959. default:
  2960. BinlPrintDbg(( DEBUG_ERRORS, "!!LdapError 0x%08x - Search failed in DNStoFQDN.\n", LdapError ));
  2961. HandleLdapFailure( LdapError,
  2962. EVENT_WARNING_LDAP_SEARCH_ERROR,
  2963. FALSE,
  2964. &LdapHandle,
  2965. FALSE ); // don't have lock
  2966. if (LdapHandle == NULL) {
  2967. if (++ldapRetryLimit < LDAP_SERVER_DOWN_LIMIT) {
  2968. goto RetryConnection;
  2969. }
  2970. }
  2971. if ( LdapMessage ) {
  2972. goto e1;
  2973. } else {
  2974. goto e0;
  2975. }
  2976. }
  2977. // Did we get a Computer Object?
  2978. count = ldap_count_entries( LdapHandle, LdapMessage );
  2979. if ( count == 0 ) {
  2980. Error = ERROR_BINL_UNABLE_TO_CONVERT;
  2981. goto e1; // nope
  2982. }
  2983. // if we get more than more entry back, we will use only the
  2984. // first one.
  2985. CurrentEntry = ldap_first_entry( LdapHandle, LdapMessage );
  2986. MachineDN = ldap_get_values( LdapHandle, CurrentEntry, ComputerAttrs[0] );
  2987. if ( !MachineDN ) {
  2988. Error = ERROR_BINL_UNABLE_TO_CONVERT;
  2989. goto e1;
  2990. }
  2991. *ppMachineDN = BinlStrDup( *MachineDN );
  2992. Error = ERROR_SUCCESS;
  2993. ldap_value_free( MachineDN );
  2994. e1:
  2995. ldap_msgfree( LdapMessage );
  2996. e0:
  2997. return Error;
  2998. }
  2999. #endif // DSCRACKNAMES_DNS
  3000. // message.c eof