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.

2096 lines
64 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. cluspw.c
  5. Abstract:
  6. cluster password utility. Co-ordinates changing the cluster service domain
  7. account password on all nodes in the cluster and updating the LSA's local
  8. password cache
  9. This implementation currently assumes that the domain of the service
  10. account and the cluster node's domain are the same (which is bad). If the
  11. two domains are different, this will affect whether the secure channel is
  12. reset (no point in resetting the channel if the password domain is
  13. different from the machine domain). This configuration increases the risk
  14. of the cluster falling apart since we're dependent upon the secure channel
  15. of node's DC to be pointed at the PDC of the account domain which seems
  16. pretty unlikely.
  17. In order to make this work reliably, we have to force replication of the
  18. password to eliminate the race between password replication and netlogon
  19. resetting the secure channel to a DC that doesn't have the updated
  20. password. Using kerberos for intra-cluster comm would help in this
  21. respect.
  22. Author:
  23. Charlie Wickham (charlwi) 22-Jul-1999
  24. Environment:
  25. User mode
  26. Revision History:
  27. --*/
  28. #define UNICODE 1
  29. #define _UNICODE 1
  30. #define CMDWINDOW
  31. #include <windows.h>
  32. #include <lmcons.h>
  33. #include <lmapibuf.h>
  34. #include <lmaccess.h>
  35. #include <windns.h>
  36. #include <stdio.h>
  37. #include <stdlib.h>
  38. #if (_WIN32_WINNT > 0x4FF)
  39. #include <dsgetdc.h>
  40. #endif
  41. #include <clusapi.h>
  42. #include <resapi.h>
  43. #include "cluspw.h"
  44. //
  45. // struct for each node in the cluster.
  46. //
  47. typedef struct _CLUSTER_NODE_DATA {
  48. struct _CLUSTER_NODE_DATA * NextNode;
  49. WCHAR NodeName[ DNS_MAX_NAME_BUFFER_LENGTH ];
  50. HNODE NodeHandle;
  51. CLUSTER_NODE_STATE NodeState;
  52. SC_HANDLE ClussvcHandle; // handle to SCM clussvc entry on this node
  53. SC_HANDLE PasswordHandle; // handle to password utility service
  54. DWORD ServiceState;
  55. } CLUSTER_NODE_DATA, *PCLUSTER_NODE_DATA;
  56. #if 0
  57. //
  58. // used to build property lists for setting group and resource command and
  59. // private properties
  60. //
  61. typedef struct _FAILOVER_PARAMBLOCK {
  62. DWORD FailoverThresholdValue;
  63. }
  64. FAILOVER_PARAMBLOCK, *PFAILOVER_PARAMBLOCK;
  65. typedef struct _RESOURCE_COMMONPROPS {
  66. DWORD RestartAction;
  67. } RESOURCE_COMMONPROPS, *PRESOURCE_COMMONPROPS;
  68. typedef struct _RESOURCE_PRIVATEPROPS {
  69. LPWSTR CommandLine;
  70. LPWSTR CurrentDirectory;
  71. DWORD InteractWithDesktop;
  72. } RESOURCE_PRIVATEPROPS, *PRESOURCE_PRIVATEPROPS;
  73. #endif
  74. PCHAR ClusterNodeState[] = {
  75. "Up",
  76. "Down",
  77. "Paused",
  78. "Joining"
  79. };
  80. /* Globals */
  81. HCLUSTER ClusterHandle;
  82. PCLUSTER_NODE_DATA NodeList;
  83. LPWSTR DomainName;
  84. LPWSTR UserName;
  85. HGROUP PWGroup; // cluster password group
  86. HRESOURCE PWResource; // cluster password resource
  87. WCHAR NodeName[ MAX_COMPUTERNAME_LENGTH + 1 ];
  88. HANDLE PipeHandle;
  89. //
  90. // cmd line args
  91. //
  92. BOOL AttemptRecovery;
  93. BOOL Unattended;
  94. DWORD StartingPhase = 1;
  95. BOOL QuietOutput;
  96. BOOL VerboseOutput;
  97. BOOL RefreshCache;
  98. LPWSTR NewPassword;
  99. LPWSTR OldPassword;
  100. LPWSTR ClusterName;
  101. BOOL RunInCmdWindow;
  102. LPWSTR ResultPipeName;
  103. VOID
  104. PrintMsg(
  105. MSG_SEVERITY Severity,
  106. LPSTR FormatString,
  107. ...
  108. )
  109. /*++
  110. Routine Description:
  111. print out the message based on the serverity of the error and the setting
  112. of QuietOutput
  113. Arguments:
  114. Severity - indicates importance level of msg
  115. FormatMessage - pointer to ANSI format string
  116. other args as appropriate
  117. Return Value:
  118. None
  119. --*/
  120. {
  121. PIPE_RESULT_MSG resultMsg;
  122. va_list ArgList;
  123. va_start(ArgList, FormatString);
  124. switch ( Severity ) {
  125. case MsgSeverityFatal:
  126. _vsnprintf( resultMsg.MsgBuf, sizeof( resultMsg.MsgBuf ), FormatString, ArgList );
  127. break;
  128. case MsgSeverityInfo:
  129. if ( !QuietOutput ) {
  130. _vsnprintf( resultMsg.MsgBuf, sizeof( resultMsg.MsgBuf ), FormatString, ArgList );
  131. }
  132. else {
  133. resultMsg.MsgBuf[0] = 0;
  134. }
  135. break;
  136. case MsgSeverityVerbose:
  137. if ( !QuietOutput && VerboseOutput ) {
  138. _vsnprintf( resultMsg.MsgBuf, sizeof( resultMsg.MsgBuf ), FormatString, ArgList );
  139. }
  140. else {
  141. resultMsg.MsgBuf[0] = 0;
  142. }
  143. break;
  144. }
  145. va_end(ArgList);
  146. if ( resultMsg.MsgBuf[0] != 0 ) {
  147. if ( RefreshCache && PipeHandle != INVALID_HANDLE_VALUE ) {
  148. BOOL success;
  149. DWORD bytesWritten;
  150. DWORD status;
  151. resultMsg.MsgType = MsgTypeString;
  152. resultMsg.Severity = Severity;
  153. wcscpy( resultMsg.NodeName, NodeName );
  154. success = WriteFile(PipeHandle,
  155. &resultMsg,
  156. sizeof( resultMsg ),
  157. &bytesWritten,
  158. NULL);
  159. if ( !success ) {
  160. status = GetLastError();
  161. printf("WriteFile failed in PrintMsg - %d\n", status );
  162. printf("%s\n", resultMsg.MsgBuf );
  163. }
  164. if ( RunInCmdWindow ) {
  165. printf( resultMsg.MsgBuf );
  166. }
  167. }
  168. else {
  169. printf( resultMsg.MsgBuf );
  170. }
  171. }
  172. } // PrintMsg
  173. DWORD
  174. RefreshPasswordCaches(
  175. VOID
  176. )
  177. /*++
  178. Routine Description:
  179. Start the password service on each node
  180. Arguments:
  181. None
  182. Return Value:
  183. None
  184. --*/
  185. {
  186. PCLUSTER_NODE_DATA nodeData;
  187. BOOL success;
  188. DWORD status = ERROR_SUCCESS;
  189. WCHAR resultPipeName[ MAX_PATH ] = L"\\\\";
  190. DWORD pipeNameSize = (sizeof(resultPipeName) / sizeof( WCHAR )) - 2;
  191. DWORD argCount;
  192. LPWSTR argVector[ 6 ];
  193. SERVICE_STATUS_PROCESS serviceStatus;
  194. DWORD bytesNeeded;
  195. BOOL continueToPoll;
  196. //
  197. // get our physical netbios name to include on the cmd line arg
  198. //
  199. #if (_WIN32_WINNT > 0x4FF)
  200. success = GetComputerNameEx(ComputerNamePhysicalNetBIOS,
  201. &resultPipeName[2],
  202. &pipeNameSize);
  203. #else
  204. success = GetComputerName( &resultPipeName[2], &pipeNameSize);
  205. #endif
  206. wcscat( resultPipeName, L"\\pipe\\cluspw" );
  207. //
  208. // loop through the cluster nodes
  209. //
  210. nodeData = NodeList;
  211. while ( nodeData != NULL ) {
  212. if ( nodeData->NodeState == ClusterNodeUp ) {
  213. PrintMsg(MsgSeverityVerbose,
  214. "Starting password service on node %ws\n",
  215. nodeData->NodeName);
  216. argCount = 0;
  217. if ( VerboseOutput ) {
  218. argVector[ argCount++ ] = L"-v";
  219. }
  220. argVector[ argCount++ ] = L"-z";
  221. argVector[ argCount++ ] = DomainName;
  222. argVector[ argCount++ ] = UserName;
  223. argVector[ argCount++ ] = NewPassword;
  224. argVector[ argCount++ ] = resultPipeName;
  225. success = StartService(nodeData->PasswordHandle,
  226. argCount,
  227. argVector);
  228. if ( !success ) {
  229. PrintMsg(MsgSeverityInfo,
  230. "Failed to start password service on node %ws - %d\n",
  231. nodeData->NodeName,
  232. GetLastError());
  233. }
  234. nodeData->ServiceState = SERVICE_START_PENDING;
  235. }
  236. nodeData = nodeData->NextNode;
  237. }
  238. #if 0
  239. // this code is of dubious use since we get back access denied on the
  240. // QuerySeviceStatusEx calls. This would follow since the password utility
  241. // has probably already updated the caches, potentially invalidating the
  242. // credentials we're using to run the client portion of the utility.
  243. //
  244. // periodically poll the node list, waiting for each service invocation to
  245. // finish
  246. do {
  247. Sleep( 1000 );
  248. nodeData = NodeList;
  249. continueToPoll = FALSE;
  250. while ( nodeData != NULL ) {
  251. if ( nodeData->NodeState == ClusterNodeUp && nodeData->ServiceState != SERVICE_STOPPED ) {
  252. if ( QueryServiceStatusEx(nodeData->PasswordHandle,
  253. SC_STATUS_PROCESS_INFO,
  254. (LPBYTE)&serviceStatus,
  255. sizeof(serviceStatus),
  256. &bytesNeeded ) )
  257. {
  258. PrintMsg(MsgSeverityInfo,
  259. "Password service state on %ws is %u\n",
  260. nodeData->NodeName,
  261. serviceStatus.dwCurrentState);
  262. nodeData->ServiceState = serviceStatus.dwCurrentState;
  263. if ( serviceStatus.dwCurrentState != SERVICE_STOPPED ) {
  264. continueToPoll = TRUE;
  265. }
  266. } else {
  267. status = GetLastError();
  268. PrintMsg(MsgSeverityInfo,
  269. "Query Service Status failed for node %ws - %u.\n",
  270. nodeData->NodeName,
  271. status );
  272. }
  273. }
  274. nodeData = nodeData->NextNode;
  275. }
  276. } while ( continueToPoll );
  277. #endif
  278. return status;
  279. } // RefreshPasswordCaches
  280. DWORD
  281. ChangePasswordWithSCMs(
  282. VOID
  283. )
  284. /*++
  285. Routine Description:
  286. Change the password with each SCM
  287. Arguments:
  288. None
  289. Return Value:
  290. None
  291. --*/
  292. {
  293. PCLUSTER_NODE_DATA nodeData;
  294. BOOL success;
  295. DWORD status = ERROR_SUCCESS;
  296. nodeData = NodeList;
  297. while ( nodeData != NULL ) {
  298. PrintMsg(MsgSeverityVerbose,
  299. "Changing SCM password on node %ws\n",
  300. nodeData->NodeName);
  301. success = ChangeServiceConfig(nodeData->ClussvcHandle,
  302. SERVICE_NO_CHANGE,
  303. SERVICE_NO_CHANGE,
  304. SERVICE_NO_CHANGE,
  305. NULL,
  306. NULL,
  307. NULL,
  308. NULL,
  309. NULL,
  310. NewPassword,
  311. NULL);
  312. if ( !success ) {
  313. status = GetLastError();
  314. PrintMsg(MsgSeverityFatal,
  315. "Problem changing password with node %ws's service controller. error %d\n",
  316. nodeData->NodeName, status);
  317. break;
  318. }
  319. nodeData = nodeData->NextNode;
  320. }
  321. return status;
  322. } // ChangePasswordWithSCMs
  323. #if 0
  324. DWORD
  325. CreatePasswordGroup(
  326. VOID
  327. )
  328. /*++
  329. Routine Description:
  330. Create a new group with a generic app resource to run cluspw on each node.
  331. Arguments:
  332. None
  333. Return Value:
  334. ERROR_SUCCESS if all went ok
  335. --*/
  336. {
  337. DWORD status;
  338. FAILOVER_PARAMBLOCK failoverBlock;
  339. RESUTIL_PROPERTY_ITEM failoverPropTable[] = {
  340. { L"FailoverThreshold", NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 0, 0,
  341. FIELD_OFFSET( FAILOVER_PARAMBLOCK, FailoverThresholdValue ) },
  342. { 0 }
  343. };
  344. DWORD bytesReturned;
  345. DWORD bytesRequired;
  346. PWGroup = CreateClusterGroup( ClusterHandle, PASSWORD_GROUP_NAME );
  347. if ( PWGroup == NULL && GetLastError() == ERROR_OBJECT_ALREADY_EXISTS ) {
  348. //
  349. // try to open the existing group
  350. //
  351. PrintMsg(MsgSeverityVerbose, "Opening existing pw group\n");
  352. PWGroup = OpenClusterGroup( ClusterHandle, PASSWORD_GROUP_NAME );
  353. }
  354. if ( PWGroup != NULL ) {
  355. PVOID failoverPropList = NULL;
  356. DWORD failoverPropListSize = 0;
  357. //
  358. // set failover threshold to zero. first call gets size needed to hold
  359. // prop list
  360. //
  361. failoverBlock.FailoverThresholdValue = 0;
  362. status = ResUtilPropertyListFromParameterBlock(failoverPropTable,
  363. NULL,
  364. &failoverPropListSize,
  365. (LPBYTE) &failoverBlock,
  366. &bytesReturned,
  367. &bytesRequired );
  368. if ( status == ERROR_MORE_DATA ) {
  369. failoverPropListSize = bytesRequired;
  370. failoverPropList = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, failoverPropListSize );
  371. status = ResUtilPropertyListFromParameterBlock(failoverPropTable,
  372. failoverPropList,
  373. &failoverPropListSize,
  374. (LPBYTE) &failoverBlock,
  375. &bytesReturned,
  376. &bytesRequired );
  377. if ( status != ERROR_SUCCESS ) {
  378. PrintMsg(MsgSeverityFatal,
  379. "Couldn't create property list to set Failover Threshold. error %d\n",
  380. status);
  381. return status;
  382. }
  383. }
  384. else if ( status != ERROR_SUCCESS ) {
  385. PrintMsg(MsgSeverityFatal,
  386. "Couldn't determine size of property list for Failover Threshold. error %d\n",
  387. status);
  388. return status;
  389. }
  390. PrintMsg(MsgSeverityVerbose, "Setting FailoverThreshold property\n");
  391. status = ClusterGroupControl(PWGroup,
  392. NULL,
  393. CLUSCTL_GROUP_SET_COMMON_PROPERTIES,
  394. failoverPropList,
  395. failoverPropListSize,
  396. NULL,
  397. 0,
  398. NULL);
  399. HeapFree( GetProcessHeap(), 0, failoverPropList );
  400. if ( status == ERROR_SUCCESS ) {
  401. //
  402. // now create the generic app resource in the group
  403. //
  404. PWResource = CreateClusterResource(PWGroup,
  405. PASSWORD_RESOURCE_NAME,
  406. L"Generic Application",
  407. 0);
  408. if ( PWResource == NULL && GetLastError() == ERROR_OBJECT_ALREADY_EXISTS ) {
  409. PrintMsg(MsgSeverityVerbose, "Opening existing pw resource\n");
  410. PWResource = OpenClusterResource(ClusterHandle,
  411. PASSWORD_RESOURCE_NAME);
  412. }
  413. if ( PWResource != NULL ) {
  414. RESOURCE_COMMONPROPS commonProps;
  415. RESUTIL_PROPERTY_ITEM commonPropTable[] = {
  416. { L"RestartAction", NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 0, 0,
  417. FIELD_OFFSET( RESOURCE_COMMONPROPS, RestartAction ) },
  418. { 0 }
  419. };
  420. PVOID propList = NULL;
  421. DWORD propListSize = 0;
  422. //
  423. // set the common props
  424. //
  425. commonProps.RestartAction = ClusterResourceDontRestart;
  426. status = ResUtilPropertyListFromParameterBlock(commonPropTable,
  427. NULL,
  428. &propListSize,
  429. (LPBYTE) &commonProps,
  430. &bytesReturned,
  431. &bytesRequired );
  432. if ( status == ERROR_MORE_DATA ) {
  433. propList = HeapAlloc( GetProcessHeap(), 0, bytesRequired );
  434. propListSize = bytesRequired;
  435. status = ResUtilPropertyListFromParameterBlock(commonPropTable,
  436. propList,
  437. &propListSize,
  438. (LPBYTE) &commonProps,
  439. &bytesReturned,
  440. &bytesRequired );
  441. if ( status != ERROR_SUCCESS ) {
  442. PrintMsg(MsgSeverityFatal,
  443. "Couldn't create property list to set Restart Action. error %d\n",
  444. status);
  445. return status;
  446. }
  447. }
  448. else if ( status != ERROR_SUCCESS ) {
  449. PrintMsg(MsgSeverityFatal,
  450. "Couldn't determine size of property list for Restart Action. error %d\n",
  451. status);
  452. return status;
  453. }
  454. PrintMsg(MsgSeverityVerbose, "Setting RestartAction property\n");
  455. status = ClusterResourceControl(PWResource,
  456. NULL,
  457. CLUSCTL_RESOURCE_SET_COMMON_PROPERTIES,
  458. propList,
  459. propListSize,
  460. NULL,
  461. 0,
  462. NULL);
  463. HeapFree( GetProcessHeap(), 0, propList );
  464. }
  465. else {
  466. status = GetLastError();
  467. PrintMsg(MsgSeverityFatal,
  468. "Couldn't create Generic Application resource for "
  469. "password utility. error %d\n",
  470. status);
  471. }
  472. }
  473. else {
  474. PrintMsg(MsgSeverityFatal,
  475. "Couldn't set failover threshold for password group. error %d\n",
  476. status);
  477. }
  478. }
  479. else {
  480. status = GetLastError();
  481. PrintMsg(MsgSeverityFatal,
  482. "Couldn't create group for password utility. error %d\n",
  483. status);
  484. }
  485. return status;
  486. } // CreatePasswordGroup
  487. #endif
  488. DWORD
  489. CopyNodeApplication(
  490. VOID
  491. )
  492. /*++
  493. Routine Description:
  494. for each node that has a valid SCM handle, copy the password cache update
  495. program to \\node\admin$\cluster. This corresponds to the node's area
  496. represented by the SystemRoot env. var.
  497. Arguments:
  498. None
  499. Return Value:
  500. None
  501. --*/
  502. {
  503. PCLUSTER_NODE_DATA nodeData;
  504. WCHAR destFile[ MAX_PATH ];
  505. WCHAR cluspwFile[ MAX_PATH ];
  506. BOOL success;
  507. DWORD status = ERROR_SUCCESS;
  508. DWORD byteCount;
  509. PrintMsg(MsgSeverityInfo, "Copying cache refresh utility to cluster nodes\n");
  510. byteCount = GetModuleFileName( NULL, cluspwFile, sizeof( cluspwFile ));
  511. if ( byteCount == 0 ) {
  512. PrintMsg(MsgSeverityFatal, "Unable to determine cluspw's file path\n");
  513. return ERROR_FILE_NOT_FOUND;
  514. }
  515. nodeData = NodeList;
  516. while ( nodeData != NULL ) {
  517. if ( nodeData->NodeState == ClusterNodeUp ) {
  518. wsprintf( destFile, L"\\\\%ws\\admin$\\" CLUWPW_SERVICE_BINARY_NAME, nodeData->NodeName );
  519. PrintMsg(MsgSeverityVerbose, "Copying %ws to %ws\n", cluspwFile, destFile);
  520. success = CopyFile( cluspwFile, destFile, FALSE );
  521. if ( !success ) {
  522. status = GetLastError();
  523. PrintMsg(MsgSeverityFatal,
  524. "Problem copying %ws to %ws. error %d\n",
  525. cluspwFile, destFile, status);
  526. break;
  527. }
  528. }
  529. nodeData = nodeData->NextNode;
  530. }
  531. return status;
  532. } // CopyNodeApplication
  533. DWORD
  534. CheckDCAvailability(
  535. VOID
  536. )
  537. /*++
  538. Routine Description:
  539. using DomainName, try to contact the DC to make sure the password change
  540. can happen
  541. Arguments:
  542. None
  543. Return Value:
  544. ERROR_SUCCESS if everything worked
  545. --*/
  546. {
  547. DWORD status;
  548. PCLUSTER_NODE_DATA nodeData;
  549. #if (_WIN32_WINNT > 0x4FF)
  550. PDOMAIN_CONTROLLER_INFO domainInfo;
  551. #else
  552. PBYTE pdcName;
  553. #endif
  554. WCHAR secureChannel[ MAX_PATH ];
  555. PWSTR newSecureChannel = secureChannel;
  556. DWORD pdcNameLength;
  557. DWORD scNameLength;
  558. DWORD nameLength;
  559. PrintMsg(MsgSeverityInfo, "Checking on Domain controller availability\n");
  560. #if (_WIN32_WINNT > 0x4FF)
  561. PrintMsg(MsgSeverityVerbose,
  562. "Calling DsGetDcName for domain %ws\n",
  563. DomainName);
  564. //
  565. // get the PDC for this domain. The password change is handled by this node.
  566. //
  567. status = DsGetDcName(NULL,
  568. DomainName,
  569. NULL, // no guid
  570. NULL, // no sitename
  571. DS_PDC_REQUIRED | DS_IS_FLAT_NAME,
  572. &domainInfo);
  573. if ( status == ERROR_NO_SUCH_DOMAIN ) {
  574. PrintMsg(MsgSeverityVerbose,
  575. "Calling DsGetDcName again with force rediscovery\n");
  576. //
  577. // try again this time specifying the rediscovery flag.
  578. //
  579. status = DsGetDcName(NULL,
  580. DomainName,
  581. NULL, // no guid
  582. NULL, // no sitename
  583. DS_FORCE_REDISCOVERY | DS_PDC_REQUIRED | DS_IS_FLAT_NAME,
  584. &domainInfo);
  585. }
  586. #else
  587. PrintMsg(MsgSeverityVerbose,
  588. "Calling NetGetDCName for domain %ws on node %ws\n",
  589. DomainName, nodeData->NodeName);
  590. status = NetGetDCName(NodeList->NodeName,
  591. DomainName,
  592. &pdcName);
  593. #endif
  594. if ( status != ERROR_SUCCESS ) {
  595. PrintMsg(MsgSeverityFatal,
  596. "Trouble contacting domain controller for %ws. error %d\n",
  597. DomainName, status);
  598. }
  599. //
  600. // change the secure channels of the cluster nodes to point to the PDC.
  601. //
  602. secureChannel[0] = UNICODE_NULL;
  603. wcscpy( secureChannel, DomainName );
  604. wcscat( secureChannel, L"\\" );
  605. wcscat( secureChannel, &domainInfo->DomainControllerName[2] );
  606. wcscat( secureChannel, L"." );
  607. wcscat( secureChannel, domainInfo->DnsForestName );
  608. pdcNameLength = wcslen( &domainInfo->DomainControllerName[2] ) +
  609. sizeof( L'.') +
  610. wcslen( domainInfo->DnsForestName );
  611. nodeData = NodeList;
  612. while ( nodeData != NULL ) {
  613. PNETLOGON_INFO_2 netlogonInfo2;
  614. //
  615. // query for the secure channel
  616. //
  617. status = I_NetLogonControl2(nodeData->NodeName,
  618. NETLOGON_CONTROL_TC_QUERY,
  619. 2,
  620. (LPBYTE)&DomainName,
  621. (LPBYTE *)&netlogonInfo2 );
  622. if ( status != ERROR_SUCCESS ) {
  623. PrintMsg(MsgSeverityFatal,
  624. "Couldn't query for secure channel. error %u\n",
  625. status);
  626. break;
  627. }
  628. scNameLength = wcslen( netlogonInfo2->netlog2_trusted_dc_name );
  629. nameLength = scNameLength <= pdcNameLength ? scNameLength : pdcNameLength;
  630. if ( _wcsnicmp(domainInfo->DomainControllerName,
  631. netlogonInfo2->netlog2_trusted_dc_name,
  632. nameLength) != 0 )
  633. {
  634. PrintMsg(MsgSeverityInfo,
  635. "Changing secure channel for node %ws from %ws to %ws\n",
  636. nodeData->NodeName,
  637. netlogonInfo2->netlog2_trusted_dc_name,
  638. domainInfo->DomainControllerName);
  639. status = I_NetLogonControl2(nodeData->NodeName,
  640. NETLOGON_CONTROL_REDISCOVER,
  641. 2,
  642. (LPBYTE)&newSecureChannel,
  643. (LPBYTE *)&netlogonInfo2 );
  644. if ( status != ERROR_SUCCESS ) {
  645. PrintMsg(MsgSeverityFatal,
  646. "Couldn't set secure channel to %ws. error %u\n",
  647. newSecureChannel, status);
  648. break;
  649. }
  650. }
  651. nodeData = nodeData->NextNode;
  652. }
  653. #if (_WIN32_WINNT > 0x4FF)
  654. NetApiBufferFree( domainInfo );
  655. #else
  656. NetApiBufferFree( pdcName );
  657. #endif
  658. return status;
  659. } // CheckDCAvailability
  660. DWORD
  661. GetClusterServiceData(
  662. LPWSTR NodeName,
  663. PCLUSTER_NODE_DATA NodeData
  664. )
  665. /*++
  666. Routine Description:
  667. Get a handle the SCM on the specified node and look up the cluster service
  668. account info if we don't have it already
  669. Arguments:
  670. NodeName - node to connect to
  671. NodeData - database entry for this node
  672. Return Value:
  673. ERROR_SUCCESS if ok
  674. --*/
  675. {
  676. SC_HANDLE scmHandle;
  677. DWORD status = ERROR_SUCCESS;
  678. //
  679. // get a handle to the SCM on this node
  680. //
  681. scmHandle = OpenSCManager( NodeName, NULL, GENERIC_WRITE );
  682. if ( scmHandle != NULL ) {
  683. SC_HANDLE svcHandle;
  684. PrintMsg(MsgSeverityVerbose, " got SCM Handle\n");
  685. //
  686. // get the domain of the cluster service account
  687. //
  688. NodeData->ClussvcHandle = OpenService(scmHandle,
  689. L"clussvc",
  690. GENERIC_WRITE |
  691. SERVICE_QUERY_CONFIG |
  692. SERVICE_CHANGE_CONFIG);
  693. if ( NodeData->ClussvcHandle != NULL ) {
  694. PrintMsg(MsgSeverityVerbose, " got SCM cluster service Handle\n");
  695. if ( DomainName == NULL ) {
  696. LPQUERY_SERVICE_CONFIG serviceConfig;
  697. DWORD bytesNeeded;
  698. BOOL success;
  699. PrintMsg(MsgSeverityVerbose, " Getting domain name\n");
  700. //
  701. // query with no buffer to get the size
  702. //
  703. success = QueryServiceConfig(NodeData->ClussvcHandle,
  704. NULL,
  705. 0,
  706. &bytesNeeded);
  707. if ( !success ) {
  708. status = GetLastError();
  709. if ( status == ERROR_INSUFFICIENT_BUFFER ) {
  710. serviceConfig = HeapAlloc( GetProcessHeap(), 0, bytesNeeded );
  711. if ( serviceConfig == NULL ) {
  712. PrintMsg(MsgSeverityFatal,
  713. "Cannot allocate memory for service config data\n");
  714. return GetLastError();
  715. }
  716. if ( QueryServiceConfig(NodeData->ClussvcHandle,
  717. serviceConfig,
  718. bytesNeeded,
  719. &bytesNeeded))
  720. {
  721. PWCHAR slash;
  722. PrintMsg(MsgSeverityVerbose,
  723. " domain account = %ws\n",
  724. serviceConfig->lpServiceStartName);
  725. DomainName = serviceConfig->lpServiceStartName;
  726. slash = wcschr( DomainName, L'\\' );
  727. if ( slash == NULL ) {
  728. PrintMsg(MsgSeverityFatal,
  729. "Can't find backslash separator in domain account string\n");
  730. return ERROR_INVALID_PARAMETER;
  731. }
  732. *slash = UNICODE_NULL;
  733. UserName = slash + 1;
  734. }
  735. //
  736. // we don't free serviceConfig since the global var DomainName
  737. // is pointing into it some where. yucky but effective
  738. //
  739. }
  740. else {
  741. PrintMsg(MsgSeverityFatal,
  742. "Unable to obtain domain name for cluster "
  743. "service account. error %d\n",
  744. status);
  745. }
  746. }
  747. else {
  748. PrintMsg(MsgSeverityFatal,
  749. "QueryServiceConfig should have failed but didn't!\n");
  750. status = ERROR_INVALID_PARAMETER;
  751. }
  752. }
  753. //
  754. // now create an entry for the password utility on this node. try
  755. // opening first just in case we didn't clean up from the last time.
  756. //
  757. if ( NodeData->NodeState == ClusterNodeUp ) {
  758. NodeData->PasswordHandle = OpenService(scmHandle,
  759. CLUSPW_SERVICE_NAME,
  760. SERVICE_START | DELETE);
  761. if ( NodeData->PasswordHandle == NULL ) {
  762. status = GetLastError();
  763. if ( status == ERROR_SERVICE_DOES_NOT_EXIST ) {
  764. DWORD serviceType;
  765. WCHAR serviceAccount[512];
  766. serviceType = SERVICE_WIN32_OWN_PROCESS;
  767. if ( RunInCmdWindow ) {
  768. serviceType |= SERVICE_INTERACTIVE_PROCESS;
  769. }
  770. wcscpy( serviceAccount, DomainName );
  771. wcscat( serviceAccount, L"\\" );
  772. wcscat( serviceAccount, UserName );
  773. NodeData->PasswordHandle = CreateService(
  774. scmHandle,
  775. CLUSPW_SERVICE_NAME,
  776. CLUSPW_DISPLAY_NAME,
  777. SERVICE_START | DELETE,
  778. serviceType,
  779. SERVICE_DEMAND_START,
  780. SERVICE_ERROR_IGNORE,
  781. L"%windir%\\" CLUWPW_SERVICE_BINARY_NAME,
  782. NULL, // name of load ordering group
  783. NULL, // receives tag identifier
  784. NULL, // array of dependency names
  785. NULL, // service account name
  786. NULL // account password
  787. );
  788. }
  789. }
  790. if ( NodeData->PasswordHandle == NULL ) {
  791. status = GetLastError();
  792. PrintMsg(MsgSeverityFatal,
  793. "Unable to open/create password service with service controller on "
  794. "node %ws. error %d\n",
  795. NodeName, status);
  796. } else {
  797. PrintMsg(MsgSeverityVerbose, " created password service\n");
  798. status = ERROR_SUCCESS;
  799. }
  800. //
  801. // call ChangeServiceConfig to set wait hint and the like
  802. //
  803. }
  804. }
  805. else {
  806. status = GetLastError();
  807. PrintMsg(MsgSeverityFatal,
  808. "Unable to open cluster service with service controller on "
  809. "node %ws. error %d\n",
  810. NodeName, status);
  811. }
  812. CloseServiceHandle( scmHandle );
  813. }
  814. else {
  815. status = GetLastError();
  816. PrintMsg(MsgSeverityFatal,
  817. "Unable to connect to service controller on node %ws. error %d\n",
  818. NodeName, status);
  819. }
  820. return status;
  821. } // GetClusterServiceData
  822. DWORD
  823. BuildNodeList(
  824. VOID
  825. )
  826. /*++
  827. Routine Description:
  828. open the cluster, get the names and states of the nodes in the cluster,
  829. and then open service controller handles to the cluster service on these
  830. nodes. create the password utility service on each node
  831. Arguments:
  832. None
  833. Return Value:
  834. ERROR_SUCCESS if everything just peachy
  835. --*/
  836. {
  837. DWORD status = ERROR_SUCCESS;
  838. PrintMsg(MsgSeverityInfo, "Opening cluster %ws\n", ClusterName );
  839. ClusterHandle = OpenCluster( ClusterName );
  840. if ( ClusterHandle != NULL ) {
  841. HCLUSENUM nodeEnum;
  842. CLUSTERVERSIONINFO clusterInfo;
  843. DWORD size = 0;
  844. //
  845. // check that there are no NT4 nodes in the cluster. We're not
  846. // prepared to deal with that just yet.
  847. //
  848. clusterInfo.dwVersionInfoSize = sizeof( CLUSTERVERSIONINFO );
  849. status = GetClusterInformation( ClusterHandle, NULL, &size, &clusterInfo );
  850. if ( status != ERROR_SUCCESS ) {
  851. PrintMsg(MsgSeverityFatal,
  852. "Failed to get cluster information: error %d\n",
  853. status);
  854. return status;
  855. }
  856. if ( CLUSTER_GET_MAJOR_VERSION( clusterInfo.dwClusterHighestVersion ) < NT5_MAJOR_VERSION ) {
  857. PrintMsg(MsgSeverityFatal, "All cluster nodes must be running Windows 2000 or later\n");
  858. return ERROR_CLUSTER_INCOMPATIBLE_VERSIONS;
  859. }
  860. //
  861. // enum the nodes in the cluster
  862. //
  863. nodeEnum = ClusterOpenEnum( ClusterHandle, CLUSTER_ENUM_NODE );
  864. if ( nodeEnum != NULL ) {
  865. DWORD enumIndex;
  866. DWORD objType;
  867. WCHAR nodeName[ MAX_COMPUTERNAME_LENGTH+1 ];
  868. DWORD nodeNameSize;
  869. for ( enumIndex = 0; ; enumIndex++ ) {
  870. nodeNameSize = sizeof( nodeName );
  871. status = ClusterEnum(nodeEnum, enumIndex, &objType, nodeName, &nodeNameSize );
  872. if ( status == ERROR_SUCCESS ) {
  873. PCLUSTER_NODE_DATA nodeData;
  874. PrintMsg(MsgSeverityVerbose,
  875. "Enum = %d, Name = %ws\n",
  876. enumIndex, nodeName);
  877. //
  878. // found a node. allocate space for node data, push it
  879. // onto the list of nodes, get its state in the
  880. // cluster. get a SCM handle to the cluster service.
  881. //
  882. nodeData = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof( CLUSTER_NODE_DATA ));
  883. if ( nodeData == NULL ) {
  884. PrintMsg(MsgSeverityFatal,
  885. "Cannot allocate memory for node data\n");
  886. status = GetLastError();
  887. break;
  888. }
  889. nodeData->NextNode = NodeList;
  890. NodeList = nodeData;
  891. wcscpy( nodeData->NodeName, nodeName );
  892. nodeData->NodeHandle = OpenClusterNode( ClusterHandle, nodeName );
  893. if ( nodeData->NodeHandle == NULL ) {
  894. status = GetLastError();
  895. PrintMsg(MsgSeverityFatal,
  896. "Cannot get handle to cluster on node %ws. error %d\n",
  897. nodeName,
  898. status);
  899. break;
  900. }
  901. nodeData->NodeState = GetClusterNodeState( nodeData->NodeHandle );
  902. if ( nodeData->NodeState == ClusterNodeStateUnknown ) {
  903. status = GetLastError();
  904. PrintMsg(MsgSeverityInfo,
  905. "Cannot determine state of cluster service on node %ws. error %d",
  906. nodeName,
  907. status);
  908. break;
  909. }
  910. else if ( nodeData->NodeState <= ClusterNodeJoining ) {
  911. PrintMsg(MsgSeverityVerbose,
  912. " state = %s\n",
  913. ClusterNodeState [nodeData->NodeState] );
  914. }
  915. PrintMsg(MsgSeverityInfo,
  916. "Node %ws is %s\n",
  917. nodeName,
  918. ClusterNodeState [nodeData->NodeState]);
  919. if ( nodeData->NodeState == ClusterNodePaused ) {
  920. PrintMsg(MsgSeverityFatal,
  921. "No node can be in the paused state\n");
  922. status = ERROR_CLUSTER_NODE_PAUSED;
  923. break;
  924. }
  925. status = GetClusterServiceData(nodeName, nodeData);
  926. if ( status != ERROR_SUCCESS ) {
  927. break;
  928. }
  929. }
  930. else if ( status == ERROR_NO_MORE_ITEMS ) {
  931. status = ERROR_SUCCESS;
  932. break;
  933. }
  934. else {
  935. PrintMsg(MsgSeverityFatal,
  936. "Failed to obtain list of nodes in cluster: error %d\n",
  937. status);
  938. break;
  939. }
  940. }
  941. ClusterCloseEnum( nodeEnum );
  942. }
  943. else {
  944. status = GetLastError();
  945. PrintMsg(MsgSeverityFatal,
  946. "Failed to get node enum handle: error %d\n",
  947. status);
  948. }
  949. }
  950. else {
  951. status = GetLastError();
  952. PrintMsg(MsgSeverityFatal, "OpenCluster failed: error %d\n", status);
  953. }
  954. return status;
  955. } // BuildNodeList
  956. DWORD
  957. BuildEveryoneSD(
  958. PSECURITY_DESCRIPTOR * SD,
  959. ULONG * SizeSD
  960. )
  961. /*++
  962. Routine Description:
  963. Build a security descriptor to control access to
  964. the cluster API
  965. Modified permissions in ACEs in order to augment cluster security
  966. administration.
  967. Arguments:
  968. SD - Returns a pointer to the created security descriptor. This
  969. should be freed by the caller.
  970. SizeSD - Returns the size in bytes of the security descriptor
  971. Return Value:
  972. ERROR_SUCCESS if successful
  973. Win32 error code otherwise
  974. --*/
  975. {
  976. DWORD Status;
  977. HANDLE Token;
  978. PACL pAcl = NULL;
  979. DWORD cbDaclSize;
  980. PSECURITY_DESCRIPTOR psd;
  981. PSECURITY_DESCRIPTOR NewSD;
  982. BYTE SDBuffer[SECURITY_DESCRIPTOR_MIN_LENGTH];
  983. PACCESS_ALLOWED_ACE pAce;
  984. PSID pOwnerSid = NULL;
  985. PSID pSystemSid = NULL;
  986. PSID pServiceSid = NULL;
  987. PULONG pSubAuthority;
  988. SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
  989. ULONG NewSDLen;
  990. psd = (PSECURITY_DESCRIPTOR) SDBuffer;
  991. //
  992. // allocate and init the SYSTEM sid
  993. //
  994. if ( !AllocateAndInitializeSid( &siaNtAuthority,
  995. 1,
  996. SECURITY_LOCAL_SYSTEM_RID,
  997. 0, 0, 0, 0, 0, 0, 0,
  998. &pSystemSid ) ) {
  999. Status = GetLastError();
  1000. goto error_exit;
  1001. }
  1002. pOwnerSid = pSystemSid;
  1003. //
  1004. // Set up the DACL that will allow admins all access.
  1005. // It should be large enough to hold 3 ACEs and their SIDs
  1006. //
  1007. cbDaclSize = ( 3 * sizeof( ACCESS_ALLOWED_ACE ) ) +
  1008. GetLengthSid( pSystemSid );
  1009. pAcl = (PACL) HeapAlloc( GetProcessHeap(), 0, cbDaclSize );
  1010. if ( pAcl == NULL ) {
  1011. Status = ERROR_NOT_ENOUGH_MEMORY;
  1012. goto error_exit;
  1013. }
  1014. InitializeSecurityDescriptor( psd, SECURITY_DESCRIPTOR_REVISION );
  1015. InitializeAcl( pAcl, cbDaclSize, ACL_REVISION );
  1016. //
  1017. // Add the ACE for the SYSTEM account to the DACL
  1018. //
  1019. if ( !AddAccessAllowedAce( pAcl,
  1020. ACL_REVISION,
  1021. GENERIC_READ | GENERIC_WRITE,
  1022. pSystemSid ) ) {
  1023. Status = GetLastError();
  1024. goto error_exit;
  1025. }
  1026. if ( !GetAce( pAcl, 0, (PVOID *) &pAce ) ) {
  1027. Status = GetLastError();
  1028. goto error_exit;
  1029. }
  1030. pAce->Header.AceFlags |= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
  1031. if ( !SetSecurityDescriptorDacl( psd, TRUE, pAcl, FALSE ) ) {
  1032. Status = GetLastError();
  1033. goto error_exit;
  1034. }
  1035. if ( !SetSecurityDescriptorOwner( psd, pOwnerSid, FALSE ) ) {
  1036. Status = GetLastError();
  1037. goto error_exit;
  1038. }
  1039. if ( !SetSecurityDescriptorGroup( psd, pOwnerSid, FALSE ) ) {
  1040. Status = GetLastError();
  1041. goto error_exit;
  1042. }
  1043. if ( !SetSecurityDescriptorSacl( psd, TRUE, NULL, FALSE ) ) {
  1044. Status = GetLastError();
  1045. goto error_exit;
  1046. }
  1047. NewSDLen = 0 ;
  1048. if ( !MakeSelfRelativeSD( psd, NULL, &NewSDLen ) ) {
  1049. Status = GetLastError();
  1050. if ( Status != ERROR_INSUFFICIENT_BUFFER ) { // Duh, we're trying to find out how big the buffer should be?
  1051. goto error_exit;
  1052. }
  1053. }
  1054. NewSD = HeapAlloc( GetProcessHeap(), 0, NewSDLen );
  1055. if ( NewSD ) {
  1056. if ( !MakeSelfRelativeSD( psd, NewSD, &NewSDLen ) ) {
  1057. Status = GetLastError();
  1058. goto error_exit;
  1059. }
  1060. Status = ERROR_SUCCESS;
  1061. *SD = NewSD;
  1062. *SizeSD = NewSDLen;
  1063. } else {
  1064. Status = ERROR_NOT_ENOUGH_MEMORY;
  1065. }
  1066. error_exit:
  1067. if ( pSystemSid != NULL ) {
  1068. FreeSid( pSystemSid );
  1069. }
  1070. if ( pAcl != NULL ) {
  1071. HeapFree( GetProcessHeap(), 0, pAcl );
  1072. }
  1073. return( Status );
  1074. } // *** BuildEveryoneSD
  1075. DWORD WINAPI
  1076. ResultPipeThread(
  1077. LPVOID Param
  1078. )
  1079. /*++
  1080. Routine Description:
  1081. Description
  1082. Arguments:
  1083. None
  1084. Return Value:
  1085. None
  1086. --*/
  1087. {
  1088. HANDLE PipeHandle;
  1089. DWORD status = ERROR_SUCCESS;
  1090. HANDLE okToGoEvent = Param;
  1091. PIPE_RESULT_MSG resultMsg;
  1092. BOOL success;
  1093. BOOL connected;
  1094. DWORD bytesRead;
  1095. #if 0
  1096. PSECURITY_DESCRIPTOR everyoneSD;
  1097. SECURITY_ATTRIBUTES secAttrib;
  1098. DWORD sdSize;
  1099. status = BuildEveryoneSD( &everyoneSD, &sdSize );
  1100. secAttrib.nLength = sizeof( secAttrib );
  1101. secAttrib.lpSecurityDescriptor = everyoneSD;
  1102. secAttrib.bInheritHandle = FALSE;
  1103. #endif
  1104. PipeHandle = CreateNamedPipe(L"\\\\.\\pipe\\cluspw",
  1105. PIPE_ACCESS_DUPLEX,
  1106. PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,
  1107. 1, // one instance
  1108. 0,
  1109. 3 * sizeof(PIPE_RESULT_MSG),
  1110. NMPWAIT_USE_DEFAULT_WAIT,
  1111. NULL /*&secAttrib*/ );
  1112. #if 0
  1113. HeapFree( GetProcessHeap(), 0, everyoneSD );
  1114. #endif
  1115. if ( PipeHandle != INVALID_HANDLE_VALUE ) {
  1116. //
  1117. // signal it is ok for the main thread to continue
  1118. //
  1119. SetEvent( okToGoEvent );
  1120. do {
  1121. connected = ConnectNamedPipe(PipeHandle, NULL) ?
  1122. TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
  1123. if ( !connected ) {
  1124. status = GetLastError();
  1125. PrintMsg(MsgSeverityFatal,
  1126. "Client failed to connect to result pipe. error %d\n",
  1127. status);
  1128. return status;
  1129. }
  1130. success = ReadFile(PipeHandle,
  1131. &resultMsg,
  1132. sizeof( resultMsg ),
  1133. &bytesRead,
  1134. NULL);
  1135. if ( !success ) {
  1136. status = GetLastError();
  1137. PrintMsg(MsgSeverityFatal,
  1138. "Failed to read from result pipe. error %d\n",
  1139. status);
  1140. DisconnectNamedPipe( PipeHandle );
  1141. continue;
  1142. }
  1143. switch ( resultMsg.MsgType ) {
  1144. case MsgTypeString:
  1145. PrintMsg(resultMsg.Severity,
  1146. "%ws: %hs",
  1147. resultMsg.NodeName,
  1148. resultMsg.MsgBuf);
  1149. break;
  1150. case MsgTypeFinalStatus:
  1151. PrintMsg(MsgSeverityInfo,
  1152. "Node %ws returned a status of %u.\n",
  1153. resultMsg.NodeName,
  1154. resultMsg.Status);
  1155. DisconnectNamedPipe( PipeHandle );
  1156. break;
  1157. default:
  1158. PrintMsg(MsgSeverityFatal,
  1159. "Received message with invalid type from node %ws\n",
  1160. resultMsg.NodeName);
  1161. }
  1162. } while (TRUE );
  1163. }
  1164. else {
  1165. status = GetLastError();
  1166. PrintMsg(MsgSeverityFatal,
  1167. "Unable to create pipe for reporting results. error %d\n",
  1168. status);
  1169. }
  1170. return status;
  1171. } // ResultPipeThread
  1172. DWORD
  1173. ParseArgs(
  1174. INT argc,
  1175. WCHAR *argv[]
  1176. )
  1177. /*++
  1178. Routine Description:
  1179. Description
  1180. Arguments:
  1181. None
  1182. Return Value:
  1183. None
  1184. --*/
  1185. {
  1186. INT i;
  1187. DWORD status = ERROR_SUCCESS;
  1188. DWORD argCount = argc - 1; // skip program name
  1189. for ( i=1; i<argc; i++ ) {
  1190. if ( argv[i][0] == '/' || argv[i][0] == '-' ) {
  1191. switch ( towupper(argv[i][1]) ) {
  1192. #ifdef CMDWINDOW
  1193. case 'C':
  1194. RunInCmdWindow = TRUE;
  1195. break;
  1196. #endif
  1197. case 'U':
  1198. Unattended = TRUE;
  1199. break;
  1200. case 'P':
  1201. if ( _wcsnicmp( argv[i]+1, L"phase", 5 ) == 0 ) {
  1202. wchar_t * numStart;
  1203. numStart = wcspbrk( argv[i], L"0123456789" );
  1204. if ( numStart != NULL ) {
  1205. StartingPhase = _wtoi( numStart );
  1206. }
  1207. else {
  1208. ++i;
  1209. StartingPhase = _wtoi( argv[i] );
  1210. }
  1211. if ( StartingPhase < 1 || StartingPhase > 3 ) {
  1212. PrintMsg( MsgSeverityFatal,
  1213. "StartingPhase must be between 1 and 3, inclusive\n" );
  1214. return ERROR_INVALID_PARAMETER;
  1215. }
  1216. }
  1217. else {
  1218. printf("Unknown option: %ws\n", argv[i]);
  1219. return ERROR_INVALID_PARAMETER;
  1220. }
  1221. break;
  1222. case 'Z':
  1223. RefreshCache = TRUE;
  1224. break;
  1225. case 'R':
  1226. AttemptRecovery = TRUE;
  1227. break;
  1228. case 'Q':
  1229. QuietOutput = TRUE;
  1230. break;
  1231. case 'V':
  1232. VerboseOutput = TRUE;
  1233. break;
  1234. default:
  1235. printf("Unknown option: %ws\n", argv[i]);
  1236. return ERROR_INVALID_PARAMETER;
  1237. }
  1238. }
  1239. else if ( RefreshCache ) {
  1240. if ( argCount > 4 ) {
  1241. PrintMsg(MsgSeverityFatal,
  1242. "Not enough args specified for password cache refresh\n");
  1243. return ERROR_INVALID_PARAMETER;
  1244. }
  1245. DomainName = argv[i];
  1246. UserName = argv[i+1];
  1247. NewPassword = argv[i+2];
  1248. ResultPipeName = argv[i+3];
  1249. break;
  1250. }
  1251. else if ( ClusterName == NULL ) {
  1252. //
  1253. // accept dot as the cluster on this node
  1254. //
  1255. if ( argv[i][0] != L'.' ) {
  1256. ClusterName = argv[i];
  1257. }
  1258. }
  1259. else if ( OldPassword == NULL ) {
  1260. OldPassword = argv[i];
  1261. }
  1262. else if ( NewPassword == NULL ) {
  1263. NewPassword = argv[i];
  1264. }
  1265. else {
  1266. printf("Too many arguments specified\n");
  1267. status = ERROR_INVALID_PARAMETER;
  1268. break;
  1269. }
  1270. --argCount;
  1271. }
  1272. PrintMsg(MsgSeverityVerbose,
  1273. "Unattend = %s, Quiet = %s, Phase = %d, Verbose = %s, Refresh = %s\n",
  1274. TrueOrFalse( Unattended ),
  1275. TrueOrFalse( QuietOutput ),
  1276. StartingPhase,
  1277. TrueOrFalse( VerboseOutput ),
  1278. TrueOrFalse( RefreshCache ));
  1279. PrintMsg(MsgSeverityVerbose,
  1280. "Recovery = %s\n",
  1281. TrueOrFalse( AttemptRecovery ));
  1282. PrintMsg(MsgSeverityVerbose,
  1283. "Cluster Name = %ws, Old Password = %ws, New Password = %ws\n",
  1284. ClusterName, OldPassword, NewPassword);
  1285. PrintMsg(MsgSeverityVerbose,
  1286. "Domain = %ws, User = %ws, ResultPipe = %ws\n",
  1287. DomainName, UserName, ResultPipeName);
  1288. //
  1289. // validate that we got we need for based on the starting phase
  1290. //
  1291. if ( RefreshCache ) {
  1292. if ( NewPassword == NULL ) {
  1293. PrintMsg(MsgSeverityFatal, "Missing password argument for -z\n" );
  1294. status = ERROR_INVALID_PARAMETER;
  1295. }
  1296. }
  1297. else if ( StartingPhase == 1 ) {
  1298. LPSTR Msg;
  1299. if ( ClusterName == NULL ) {
  1300. Msg = "Cluster Name argument is missing\n";
  1301. }
  1302. else if ( OldPassword == NULL ) {
  1303. Msg = "Old password argument is missing\n";
  1304. }
  1305. else if ( NewPassword == NULL) {
  1306. Msg = "New password argument is missing\n";
  1307. }
  1308. else {
  1309. Msg = NULL;
  1310. }
  1311. if ( Msg != NULL ) {
  1312. PrintMsg(MsgSeverityFatal, Msg );
  1313. status = ERROR_INVALID_PARAMETER;
  1314. }
  1315. }
  1316. if ( QuietOutput && VerboseOutput ) {
  1317. PrintMsg(MsgSeverityFatal, "Quiet and verbose options are mutally exclusive\n");
  1318. status = ERROR_INVALID_PARAMETER;
  1319. }
  1320. return status;
  1321. } // ParseArgs
  1322. VOID
  1323. PrintUsage(
  1324. VOID
  1325. )
  1326. /*++
  1327. Routine Description:
  1328. print the help msg
  1329. Arguments:
  1330. None
  1331. Return Value:
  1332. None
  1333. --*/
  1334. {
  1335. printf("\n");
  1336. printf("cluspw [/quiet] [/verbose] [/phase#] <cluster name> <old password> <new password>\n");
  1337. printf(" /quiet - quiet mode; only print errors\n");
  1338. printf(" /verbose - verbose mode; extra info\n");
  1339. printf(" /phase - starting phase: 1, 2, or 3. Default is 1\n");
  1340. printf(" Phase 1: set the password at the DC\n");
  1341. printf(" Phase 2: update the password caches on each cluster node\n");
  1342. printf(" Phase 3: update the password with each node's service controller\n");
  1343. } // PrintUsage
  1344. VOID
  1345. CleanUp(
  1346. VOID
  1347. )
  1348. /*++
  1349. Routine Description:
  1350. remove the turds we left around
  1351. Arguments:
  1352. None
  1353. Return Value:
  1354. None
  1355. --*/
  1356. {
  1357. PCLUSTER_NODE_DATA nodeData;
  1358. WCHAR destFile[ MAX_PATH ];
  1359. DWORD status;
  1360. CLUSTER_RESOURCE_STATE resState;
  1361. BOOL bSuccess;
  1362. //
  1363. // cleanup the broker program if it was copied to the node.
  1364. //
  1365. nodeData = NodeList;
  1366. while ( nodeData != NULL ) {
  1367. if ( nodeData->ClussvcHandle != NULL ) {
  1368. if ( nodeData->NodeState == ClusterNodeUp && StartingPhase < 3 ) {
  1369. wsprintf( destFile, L"\\\\%ws\\admin$\\" CLUWPW_SERVICE_BINARY_NAME, nodeData->NodeName );
  1370. PrintMsg(MsgSeverityVerbose, "Deleting %ws\n", destFile);
  1371. DeleteFile( destFile );
  1372. }
  1373. if ( nodeData->PasswordHandle != NULL ) {
  1374. bSuccess = DeleteService( nodeData->PasswordHandle );
  1375. if ( !bSuccess ) {
  1376. PrintMsg(MsgSeverityInfo,
  1377. "Unable to delete cluster password service entry on %ws - status %d\n",
  1378. nodeData->NodeName,
  1379. GetLastError());
  1380. }
  1381. CloseServiceHandle( nodeData->PasswordHandle );
  1382. }
  1383. CloseServiceHandle( nodeData->ClussvcHandle );
  1384. }
  1385. if ( nodeData->NodeHandle != NULL ) {
  1386. CloseClusterNode( nodeData->NodeHandle );
  1387. }
  1388. nodeData = nodeData->NextNode;
  1389. }
  1390. if ( ClusterHandle != NULL ) {
  1391. CloseCluster( ClusterHandle );
  1392. }
  1393. } // CleanUp
  1394. int __cdecl
  1395. wmain(
  1396. int argc,
  1397. WCHAR *argv[]
  1398. )
  1399. /*++
  1400. Routine Description:
  1401. main routine for utility
  1402. Arguments:
  1403. standard command line args
  1404. Return Value:
  1405. 0 if it worked successfully
  1406. --*/
  1407. {
  1408. DWORD status;
  1409. DWORD waitStatus;
  1410. NET_API_STATUS netStatus;
  1411. HANDLE pipeThread;
  1412. HANDLE okToGoEvent;
  1413. DWORD threadId;
  1414. HANDLE handleArray[2];
  1415. PWCHAR invokedAs;
  1416. //
  1417. // checked to see how we were invoked.
  1418. //
  1419. invokedAs = wcsrchr( argv[0], L'\\' );
  1420. if ( invokedAs == NULL ) {
  1421. invokedAs = argv[0];
  1422. } else {
  1423. ++invokedAs;
  1424. }
  1425. if ( argc == 1 ) {
  1426. if ( _wcsicmp( invokedAs, CLUWPW_SERVICE_BINARY_NAME ) == 0 ) {
  1427. ServiceStartup();
  1428. } else {
  1429. PrintUsage();
  1430. return ERROR_INVALID_PARAMETER;
  1431. }
  1432. }
  1433. else {
  1434. status = ParseArgs( argc, argv );
  1435. if ( status != ERROR_SUCCESS ) {
  1436. PrintUsage();
  1437. return status;
  1438. }
  1439. //
  1440. // create an event for the result pipe thread to signal that is it
  1441. // ready to receive msgs
  1442. //
  1443. okToGoEvent = CreateEvent(NULL, // no security
  1444. FALSE, // auto-reset
  1445. FALSE, // not signalled
  1446. NULL); // no name
  1447. if ( okToGoEvent == NULL ) {
  1448. status = GetLastError();
  1449. PrintMsg(MsgSeverityFatal, "Couldn't create \"Ok to go\" event. error %d\n",
  1450. status);
  1451. return status;
  1452. }
  1453. //
  1454. // create a thread for the routine that creates a named pipe used by the
  1455. // clients to report their status
  1456. //
  1457. pipeThread = CreateThread(NULL,
  1458. 0,
  1459. ResultPipeThread,
  1460. okToGoEvent,
  1461. 0,
  1462. &threadId);
  1463. if ( pipeThread == NULL ) {
  1464. status = GetLastError();
  1465. PrintMsg(MsgSeverityFatal, "Couldn't create thread for result pipe. error %d\n",
  1466. status);
  1467. return status;
  1468. }
  1469. //
  1470. // now wait for one to be signalled. If it's the event, then all is
  1471. // well. Otherwise, our pipe thread died.
  1472. //
  1473. handleArray[0] = pipeThread;
  1474. handleArray[1] = okToGoEvent;
  1475. status = WaitForMultipleObjects( 2, handleArray, FALSE, INFINITE );
  1476. if (( status - WAIT_OBJECT_0 ) == 0 ) {
  1477. goto error_exit;
  1478. }
  1479. //
  1480. // find all the nodes in the specified cluster and build up a database
  1481. // (among other things) about them
  1482. //
  1483. status = BuildNodeList();
  1484. if ( status != ERROR_SUCCESS ) {
  1485. return status;
  1486. }
  1487. status = CheckDCAvailability();
  1488. if ( status != ERROR_SUCCESS ) {
  1489. return status;
  1490. }
  1491. if ( StartingPhase != 3 ) {
  1492. status = CopyNodeApplication();
  1493. if ( status != ERROR_SUCCESS ) {
  1494. goto error_exit;
  1495. }
  1496. #if 0
  1497. status = CreatePasswordGroup();
  1498. if ( status != ERROR_SUCCESS ) {
  1499. goto error_exit;
  1500. }
  1501. #endif
  1502. }
  1503. switch ( StartingPhase ) {
  1504. case 1:
  1505. //
  1506. // change password at DC
  1507. //
  1508. PrintMsg(MsgSeverityInfo, "Phase 1: Changing password at DC\n");
  1509. netStatus = NetUserChangePassword(DomainName,
  1510. UserName,
  1511. OldPassword,
  1512. NewPassword);
  1513. if ( netStatus != ERROR_SUCCESS ) {
  1514. PrintMsg(MsgSeverityFatal,
  1515. "Couldn't change the password at the domain controller. error %d\n",
  1516. netStatus);
  1517. goto error_exit;
  1518. }
  1519. case 2:
  1520. //
  1521. // run the broker to change the password cache. we have to
  1522. // continue if the broker failed since the p/w has been changed at
  1523. // the DC. If the cluster falls apart, we still need to reset the
  1524. // SCM's p/w so it can restart
  1525. //
  1526. PrintMsg(MsgSeverityInfo,
  1527. "Phase 2: Refreshing password cache on each cluster node.\n");
  1528. RefreshPasswordCaches();
  1529. case 3:
  1530. PrintMsg(MsgSeverityInfo,
  1531. "Phase 3: Updating password with Service Controller on each cluster node.\n");
  1532. status = ChangePasswordWithSCMs();
  1533. }
  1534. error_exit:
  1535. //
  1536. // see if the pipe handle was signalled and report its status
  1537. //
  1538. waitStatus = WaitForSingleObject( pipeThread, 0 );
  1539. if ( waitStatus == WAIT_OBJECT_0 ) {
  1540. GetExitCodeThread( pipeThread, &status );
  1541. }
  1542. CleanUp();
  1543. }
  1544. return status;
  1545. } // wmain
  1546. #if 0
  1547. //
  1548. // old version that tried to use genapp resources. Keeping this code around
  1549. // since it shows how to use prop lists
  1550. //
  1551. DWORD
  1552. RefreshPasswordCaches(
  1553. VOID
  1554. )
  1555. /*++
  1556. Routine Description:
  1557. Start our service on each node
  1558. Arguments:
  1559. None
  1560. Return Value:
  1561. None
  1562. --*/
  1563. {
  1564. PCLUSTER_NODE_DATA nodeData;
  1565. BOOL success;
  1566. DWORD status;
  1567. CLUSTER_RESOURCE_STATE resState;
  1568. PVOID propList = NULL;
  1569. DWORD propListSize = 0;
  1570. WCHAR cmdBuff[ 512 ];
  1571. RESOURCE_PRIVATEPROPS privateProps;
  1572. RESUTIL_PROPERTY_ITEM privatePropTable[] = {
  1573. { L"CommandLine", NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0,
  1574. FIELD_OFFSET( RESOURCE_PRIVATEPROPS, CommandLine ) },
  1575. { L"CurrentDirectory", NULL, CLUSPROP_FORMAT_SZ, 0, 0, 0, 0,
  1576. FIELD_OFFSET( RESOURCE_PRIVATEPROPS, CurrentDirectory ) },
  1577. { L"InteractWithDesktop", NULL, CLUSPROP_FORMAT_DWORD, 0, 0, 0, 0,
  1578. FIELD_OFFSET( RESOURCE_PRIVATEPROPS, InteractWithDesktop ) },
  1579. { 0 }
  1580. };
  1581. DWORD bytesReturned;
  1582. DWORD bytesRequired;
  1583. WCHAR resultPipeName[ MAX_PATH ] = L"\\\\";
  1584. DWORD pipeNameSize = (sizeof(resultPipeName) / sizeof( WCHAR )) - 2;
  1585. //
  1586. // get our physical netbios name to include on the cmd line arg
  1587. //
  1588. #if (_WIN32_WINNT > 0x4FF)
  1589. success = GetComputerNameEx(ComputerNamePhysicalNetBIOS,
  1590. &resultPipeName[2],
  1591. &pipeNameSize);
  1592. #else
  1593. success = GetComputerName( &resultPipeName[2], &pipeNameSize);
  1594. #endif
  1595. wcscat( resultPipeName, L"\\pipe\\cluspw" );
  1596. //
  1597. // loop through the cluster nodes
  1598. //
  1599. nodeData = NodeList;
  1600. while ( nodeData != NULL ) {
  1601. if ( nodeData->NodeState == ClusterNodeUp ) {
  1602. PrintMsg(MsgSeverityVerbose,
  1603. "Moving PW Group to node %ws\n",
  1604. nodeData->NodeName);
  1605. status = MoveClusterGroup( PWGroup, nodeData->NodeHandle );
  1606. if ( status != ERROR_SUCCESS ) {
  1607. PrintMsg(MsgSeverityFatal,
  1608. "Problem moving Password group to node %ws. error %d\n",
  1609. nodeData->NodeName, status);
  1610. break;
  1611. }
  1612. //
  1613. // set the private props for our resource. The app is copied to
  1614. // the admin$ share on each node which is on the default path that
  1615. // is given to all users.
  1616. //
  1617. wsprintf(cmdBuff,
  1618. L"%wscluspw.exe %ws-z %ws %ws %ws %ws",
  1619. RunInCmdWindow ? L"cmd /k " : L"",
  1620. nodeData->NodeName,
  1621. VerboseOutput ? L"-v " : L"",
  1622. DomainName,
  1623. UserName,
  1624. NewPassword,
  1625. resultPipeName);
  1626. privateProps.InteractWithDesktop = RunInCmdWindow;
  1627. privateProps.CommandLine = cmdBuff;
  1628. PrintMsg(MsgSeverityVerbose, "cmd line: %ws\n", cmdBuff );
  1629. privateProps.CurrentDirectory = L".";
  1630. propListSize = 0;
  1631. status = ResUtilPropertyListFromParameterBlock(privatePropTable,
  1632. NULL,
  1633. &propListSize,
  1634. (LPBYTE) &privateProps,
  1635. &bytesReturned,
  1636. &bytesRequired );
  1637. if ( status == ERROR_MORE_DATA ) {
  1638. propList = HeapAlloc( GetProcessHeap(), 0, bytesRequired );
  1639. propListSize = bytesRequired;
  1640. status = ResUtilPropertyListFromParameterBlock(privatePropTable,
  1641. propList,
  1642. &propListSize,
  1643. (LPBYTE) &privateProps,
  1644. &bytesReturned,
  1645. &bytesRequired );
  1646. if ( status != ERROR_SUCCESS ) {
  1647. PrintMsg(MsgSeverityFatal,
  1648. "Couldn't create property list to set Generic App properties. error %d\n",
  1649. status);
  1650. return status;
  1651. }
  1652. }
  1653. else if ( status != ERROR_SUCCESS ) {
  1654. PrintMsg(MsgSeverityFatal,
  1655. "Couldn't determine size of property list for Generic App properties. error %d\n",
  1656. status);
  1657. return status;
  1658. }
  1659. PrintMsg(MsgSeverityVerbose, "Setting GenApp properties\n");
  1660. status = ClusterResourceControl(PWResource,
  1661. NULL,
  1662. CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES,
  1663. propList,
  1664. propListSize,
  1665. NULL,
  1666. 0,
  1667. NULL);
  1668. HeapFree( GetProcessHeap(), 0, propList );
  1669. status = OnlineClusterResource( PWResource );
  1670. if ( status == ERROR_IO_PENDING || status == ERROR_SUCCESS ) {
  1671. //
  1672. // wait until the resource has finished running
  1673. //
  1674. do {
  1675. Sleep( 250 );
  1676. resState = GetClusterResourceState(PWResource, NULL, NULL, NULL, NULL);
  1677. if ( resState == ClusterResourceFailed || resState == ClusterResourceOffline ) {
  1678. break;
  1679. }
  1680. } while ( TRUE );
  1681. status = ERROR_SUCCESS;
  1682. }
  1683. else {
  1684. PrintMsg(MsgSeverityFatal,
  1685. "Problem bringing Password resource online on node %ws. error %d\n",
  1686. nodeData->NodeName, status);
  1687. break;
  1688. }
  1689. }
  1690. nodeData = nodeData->NextNode;
  1691. }
  1692. return status;
  1693. } // RefreshPasswordCaches
  1694. #endif
  1695. /* end cluspw.c */