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.

858 lines
19 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. nmutil.c
  5. Abstract:
  6. Miscellaneous utility routines for the Node Manager component.
  7. Author:
  8. Mike Massa (mikemas) 26-Oct-1996
  9. Revision History:
  10. --*/
  11. #define UNICODE 1
  12. #include "service.h"
  13. #include "nmp.h"
  14. #include <ntlsa.h>
  15. #include <ntmsv1_0.h>
  16. PVOID NmpClusterKey = NULL;
  17. DWORD NmpClusterKeyLength = 0;
  18. DWORD
  19. NmpQueryString(
  20. IN HDMKEY Key,
  21. IN LPCWSTR ValueName,
  22. IN DWORD ValueType,
  23. IN LPWSTR *StringBuffer,
  24. IN OUT LPDWORD StringBufferSize,
  25. OUT LPDWORD StringSize
  26. )
  27. /*++
  28. Routine Description:
  29. Reads a REG_SZ or REG_MULTI_SZ registry value. If the StringBuffer is
  30. not large enough to hold the data, it is reallocated.
  31. Arguments:
  32. Key - Open key for the value to be read.
  33. ValueName - Unicode name of the value to be read.
  34. ValueType - REG_SZ or REG_MULTI_SZ.
  35. StringBuffer - Buffer into which to place the value data.
  36. StringBufferSize - Pointer to the size of the StringBuffer. This parameter
  37. is updated if StringBuffer is reallocated.
  38. StringSize - The size of the data returned in StringBuffer, including
  39. the terminating null character.
  40. Return Value:
  41. The status of the registry query.
  42. Notes:
  43. To avoid deadlock with DM, must not be called with NM lock held.
  44. --*/
  45. {
  46. DWORD status;
  47. DWORD valueType;
  48. WCHAR *temp;
  49. DWORD oldBufferSize = *StringBufferSize;
  50. BOOL noBuffer = FALSE;
  51. if (*StringBufferSize == 0) {
  52. noBuffer = TRUE;
  53. }
  54. *StringSize = *StringBufferSize;
  55. status = DmQueryValue( Key,
  56. ValueName,
  57. &valueType,
  58. (LPBYTE) *StringBuffer,
  59. StringSize
  60. );
  61. if (status == NO_ERROR) {
  62. if (!noBuffer ) {
  63. if (valueType == ValueType) {
  64. return(NO_ERROR);
  65. }
  66. else {
  67. return(ERROR_INVALID_PARAMETER);
  68. }
  69. }
  70. status = ERROR_MORE_DATA;
  71. }
  72. if (status == ERROR_MORE_DATA) {
  73. temp = MIDL_user_allocate(*StringSize);
  74. if (temp == NULL) {
  75. *StringSize = 0;
  76. return(ERROR_NOT_ENOUGH_MEMORY);
  77. }
  78. if (!noBuffer) {
  79. MIDL_user_free(*StringBuffer);
  80. }
  81. *StringBuffer = temp;
  82. *StringBufferSize = *StringSize;
  83. status = DmQueryValue( Key,
  84. ValueName,
  85. &valueType,
  86. (LPBYTE) *StringBuffer,
  87. StringSize
  88. );
  89. if (status == NO_ERROR) {
  90. if (valueType == ValueType) {
  91. return(NO_ERROR);
  92. }
  93. else {
  94. *StringSize = 0;
  95. return(ERROR_INVALID_PARAMETER);
  96. }
  97. }
  98. }
  99. return(status);
  100. } // NmpQueryString
  101. //
  102. // Routines to support the common network configuration code.
  103. //
  104. VOID
  105. ClNetPrint(
  106. IN ULONG LogLevel,
  107. IN PCHAR FormatString,
  108. ...
  109. )
  110. {
  111. CHAR buffer[256];
  112. DWORD bytes;
  113. va_list argList;
  114. va_start(argList, FormatString);
  115. bytes = FormatMessageA(
  116. FORMAT_MESSAGE_FROM_STRING,
  117. FormatString,
  118. 0,
  119. 0,
  120. buffer,
  121. sizeof(buffer),
  122. &argList
  123. );
  124. va_end(argList);
  125. if (bytes != 0) {
  126. ClRtlLogPrint(LogLevel, "%1!hs!", buffer);
  127. }
  128. return;
  129. } // ClNetPrint
  130. VOID
  131. ClNetLogEvent(
  132. IN DWORD LogLevel,
  133. IN DWORD MessageId
  134. )
  135. {
  136. CsLogEvent(LogLevel, MessageId);
  137. return;
  138. } // ClNetLogEvent
  139. VOID
  140. ClNetLogEvent1(
  141. IN DWORD LogLevel,
  142. IN DWORD MessageId,
  143. IN LPCWSTR Arg1
  144. )
  145. {
  146. CsLogEvent1(LogLevel, MessageId, Arg1);
  147. return;
  148. } // ClNetLogEvent1
  149. VOID
  150. ClNetLogEvent2(
  151. IN DWORD LogLevel,
  152. IN DWORD MessageId,
  153. IN LPCWSTR Arg1,
  154. IN LPCWSTR Arg2
  155. )
  156. {
  157. CsLogEvent2(LogLevel, MessageId, Arg1, Arg2);
  158. return;
  159. } // ClNetLogEvent2
  160. VOID
  161. ClNetLogEvent3(
  162. IN DWORD LogLevel,
  163. IN DWORD MessageId,
  164. IN LPCWSTR Arg1,
  165. IN LPCWSTR Arg2,
  166. IN LPCWSTR Arg3
  167. )
  168. {
  169. CsLogEvent3(LogLevel, MessageId, Arg1, Arg2, Arg3);
  170. return;
  171. } // ClNetLogEvent3
  172. BOOLEAN
  173. NmpLockedEnterApi(
  174. NM_STATE RequiredState
  175. )
  176. {
  177. if (NmpState >= RequiredState) {
  178. NmpActiveThreadCount++;
  179. CL_ASSERT(NmpActiveThreadCount != 0);
  180. return(TRUE);
  181. }
  182. return(FALSE);
  183. } // NmpLockedEnterApi
  184. BOOLEAN
  185. NmpEnterApi(
  186. NM_STATE RequiredState
  187. )
  188. {
  189. BOOLEAN mayEnter;
  190. NmpAcquireLock();
  191. mayEnter = NmpLockedEnterApi(RequiredState);
  192. NmpReleaseLock();
  193. return(mayEnter);
  194. } // NmpEnterApi
  195. VOID
  196. NmpLockedLeaveApi(
  197. VOID
  198. )
  199. {
  200. CL_ASSERT(NmpActiveThreadCount > 0);
  201. NmpActiveThreadCount--;
  202. if ((NmpActiveThreadCount == 0) && (NmpState == NmStateOfflinePending)) {
  203. SetEvent(NmpShutdownEvent);
  204. }
  205. return;
  206. } // NmpLockedLeaveApi
  207. VOID
  208. NmpLeaveApi(
  209. VOID
  210. )
  211. {
  212. NmpAcquireLock();
  213. NmpLockedLeaveApi();
  214. NmpReleaseLock();
  215. return;
  216. } // NmpLeaveApi
  217. //
  218. // Routines to provide a cluster shared key for signing and encrypting
  219. // data.
  220. //
  221. DWORD
  222. NmpGetLogonId(
  223. OUT LUID * LogonId
  224. )
  225. {
  226. HANDLE tokenHandle = NULL;
  227. TOKEN_STATISTICS tokenInfo;
  228. DWORD bytesReturned;
  229. BOOL success = FALSE;
  230. DWORD status;
  231. if (LogonId == NULL) {
  232. status = STATUS_UNSUCCESSFUL;
  233. goto error_exit;
  234. }
  235. if (!OpenProcessToken(
  236. GetCurrentProcess(),
  237. TOKEN_QUERY,
  238. &tokenHandle
  239. )) {
  240. status = GetLastError();
  241. ClRtlLogPrint(LOG_UNUSUAL,
  242. "[NM] Failed to open process token, status %1!u!.\n",
  243. status
  244. );
  245. goto error_exit;
  246. }
  247. if (!GetTokenInformation(
  248. tokenHandle,
  249. TokenStatistics,
  250. &tokenInfo,
  251. sizeof(tokenInfo),
  252. &bytesReturned
  253. )) {
  254. status = GetLastError();
  255. ClRtlLogPrint(LOG_UNUSUAL,
  256. "[NM] Failed to get token information, status %1!u!.\n",
  257. status
  258. );
  259. goto error_exit;
  260. }
  261. RtlCopyMemory(LogonId, &(tokenInfo.AuthenticationId), sizeof(LUID));
  262. status = STATUS_SUCCESS;
  263. error_exit:
  264. if (tokenHandle != NULL) {
  265. CloseHandle(tokenHandle);
  266. }
  267. return(status);
  268. } // NmpGetLogonId
  269. DWORD
  270. NmpGenerateClusterKey(
  271. IN PVOID MixingBytes,
  272. IN DWORD MixingBytesSize,
  273. OUT PVOID * Key,
  274. OUT DWORD * KeyLength
  275. )
  276. /*++
  277. Routine Description:
  278. Generate the cluster key using the cluster instance id
  279. as mixing bytes. Allocate a buffer for the key and
  280. return it.
  281. Arguments:
  282. Key - set to buffer containing key
  283. KeyLength - length of resulting key
  284. --*/
  285. {
  286. LUID logonId;
  287. BOOLEAN wasEnabled = FALSE;
  288. BOOLEAN trusted = FALSE;
  289. STRING name;
  290. HANDLE lsaHandle = NULL;
  291. DWORD ignore;
  292. DWORD packageId = 0;
  293. DWORD requestSize;
  294. PMSV1_0_DERIVECRED_REQUEST request = NULL;
  295. DWORD responseSize;
  296. PMSV1_0_DERIVECRED_RESPONSE response = NULL;
  297. PUCHAR key;
  298. DWORD keyLength;
  299. DWORD status = STATUS_SUCCESS;
  300. DWORD subStatus = STATUS_SUCCESS;
  301. status = NmpGetLogonId(&logonId);
  302. if (!NT_SUCCESS(status)) {
  303. ClRtlLogPrint(
  304. LOG_UNUSUAL,
  305. "[NM] Failed to determine logon ID, status %1!u!.\n",
  306. status
  307. );
  308. goto error_exit;
  309. }
  310. //
  311. // Try to turn on TCB privilege if running in console mode.
  312. //
  313. // ISSUE-2001/04/30-daviddio
  314. // In normal operation, there is no need to enable the TCB privilege.
  315. // In fact, enabling the TCB privilege should usually fail since the
  316. // cluster service account is not given the TCB privilege during setup.
  317. // The fix for bug 337751 allows the cluster service account to issue
  318. // a MSV1_0_DERIVECRED_REQUEST even if it does not have a trusted
  319. // connection to LSA. The reason this code is left in is for
  320. // trouble-shooting. If the cluster service is run from the command
  321. // line (e.g. not via the SCM), then the fix for bug 337751 will not
  322. // apply, and a trusted connection to LSA will be required to generate
  323. // the cluster key.
  324. //
  325. if (CsRunningAsService) {
  326. status = RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, FALSE, &wasEnabled);
  327. if (!NT_SUCCESS(status)) {
  328. #if CLUSTER_BETA
  329. ClRtlLogPrint(LOG_NOISE,
  330. "[NM] Failed to turn on TCB privilege, status %1!u!.\n",
  331. LsaNtStatusToWinError(status)
  332. );
  333. #endif // CLUSTER_BETA
  334. trusted = FALSE;
  335. } else {
  336. #if CLUSTER_BETA
  337. ClRtlLogPrint(LOG_NOISE,
  338. "[NM] Turned on TCB privilege, wasEnabled = %1!ws!.\n",
  339. (wasEnabled) ? L"TRUE" : L"FALSE"
  340. );
  341. #endif // CLUSTER_BETA
  342. trusted = TRUE;
  343. }
  344. }
  345. //
  346. // Establish contact with LSA.
  347. //
  348. if (trusted) {
  349. RtlInitString(&name, "ClusSvcNM");
  350. status = LsaRegisterLogonProcess(&name, &lsaHandle, &ignore);
  351. //
  352. // Turn off TCB privilege
  353. //
  354. if (!wasEnabled) {
  355. subStatus = RtlAdjustPrivilege(
  356. SE_TCB_PRIVILEGE,
  357. FALSE,
  358. FALSE,
  359. &wasEnabled
  360. );
  361. if (!NT_SUCCESS(subStatus)) {
  362. ClRtlLogPrint(
  363. LOG_UNUSUAL,
  364. "[NM] Failed to disable TCB privilege, "
  365. "status %1!u!.\n",
  366. subStatus
  367. );
  368. }
  369. }
  370. }
  371. else {
  372. status = LsaConnectUntrusted(&lsaHandle);
  373. }
  374. if (!NT_SUCCESS(status)) {
  375. status = LsaNtStatusToWinError(status);
  376. ClRtlLogPrint(LOG_UNUSUAL,
  377. "[NM] Failed to obtain LSA logon handle in %1!ws! mode, "
  378. "status %2!u!.\n",
  379. (trusted) ? L"trusted" : L"untrusted", status
  380. );
  381. goto error_exit;
  382. }
  383. //
  384. // Lookup the authentication package.
  385. //
  386. RtlInitString( &name, MSV1_0_PACKAGE_NAME );
  387. status = LsaLookupAuthenticationPackage(lsaHandle, &name, &packageId);
  388. if (!NT_SUCCESS(status)) {
  389. status = LsaNtStatusToWinError(status);
  390. ClRtlLogPrint(LOG_UNUSUAL,
  391. "[NM] Failed to local authentication package with "
  392. "name %1!ws!, status %2!u!.\n",
  393. name.Buffer, status
  394. );
  395. goto error_exit;
  396. }
  397. //
  398. // Build the derive credentials request with the provided
  399. // mixing bytes.
  400. //
  401. requestSize = sizeof(MSV1_0_DERIVECRED_REQUEST) + MixingBytesSize;
  402. request = LocalAlloc(LMEM_FIXED, requestSize);
  403. if (request == NULL) {
  404. ClRtlLogPrint(LOG_UNUSUAL,
  405. "[NM] Failed to allocate LSA request of size %1!u! bytes.\n"
  406. );
  407. status = ERROR_NOT_ENOUGH_MEMORY;
  408. goto error_exit;
  409. }
  410. request->MessageType = MsV1_0DeriveCredential;
  411. RtlCopyMemory(&(request->LogonId), &logonId, sizeof(logonId));
  412. request->DeriveCredType = MSV1_0_DERIVECRED_TYPE_SHA1;
  413. request->DeriveCredInfoLength = MixingBytesSize;
  414. RtlCopyMemory(
  415. &(request->DeriveCredSubmitBuffer[0]),
  416. MixingBytes,
  417. MixingBytesSize
  418. );
  419. //
  420. // Make the call through LSA to the authentication package.
  421. //
  422. status = LsaCallAuthenticationPackage(
  423. lsaHandle,
  424. packageId,
  425. request,
  426. requestSize,
  427. &response,
  428. &responseSize,
  429. &subStatus
  430. );
  431. if (!NT_SUCCESS(status)) {
  432. status = LsaNtStatusToWinError(status);
  433. subStatus = LsaNtStatusToWinError(subStatus);
  434. ClRtlLogPrint(LOG_UNUSUAL,
  435. "[NM] DeriveCredential call to authentication "
  436. "package failed, status %1!u!, auth package "
  437. "status %2!u!.\n", status, subStatus
  438. );
  439. goto error_exit;
  440. }
  441. //
  442. // Allocate a non-LSA buffer to store the key.
  443. //
  444. keyLength = response->DeriveCredInfoLength;
  445. key = MIDL_user_allocate(keyLength);
  446. if (key == NULL) {
  447. status = ERROR_NOT_ENOUGH_MEMORY;
  448. ClRtlLogPrint(LOG_UNUSUAL,
  449. "[NM] Failed to allocate buffer for cluster "
  450. "key of size %1!u!.\n",
  451. keyLength
  452. );
  453. goto error_exit;
  454. }
  455. //
  456. // Store the derived credentials in the key buffer.
  457. //
  458. RtlCopyMemory(key, &(response->DeriveCredReturnBuffer[0]), keyLength);
  459. //
  460. // Zero the derived credential buffer to be extra paranoid.
  461. //
  462. RtlZeroMemory(
  463. &(response->DeriveCredReturnBuffer[0]),
  464. response->DeriveCredInfoLength
  465. );
  466. status = STATUS_SUCCESS;
  467. *Key = key;
  468. *KeyLength = keyLength;
  469. error_exit:
  470. if (lsaHandle != NULL) {
  471. LsaDeregisterLogonProcess(lsaHandle);
  472. lsaHandle = NULL;
  473. }
  474. if (request != NULL) {
  475. LocalFree(request);
  476. request = NULL;
  477. }
  478. if (response != NULL) {
  479. LsaFreeReturnBuffer(response);
  480. response = NULL;
  481. }
  482. return(status);
  483. } // NmpGenerateClusterKey
  484. DWORD
  485. NmpGetClusterKey(
  486. OUT PVOID KeyBuffer,
  487. IN OUT DWORD * KeyBufferLength
  488. )
  489. /*++
  490. Routine Description:
  491. Copy the shared cluster key into the buffer provided.
  492. Arguments:
  493. KeyBuffer - buffer to which key should be copied
  494. KeyBufferLength - IN: length of KeyBuffer
  495. OUT: required buffer size, if input
  496. buffer length is insufficient
  497. Return value:
  498. ERROR_INSUFFICIENT_BUFFER if KeyBuffer is too small.
  499. ERROR_FILE_NOT_FOUND if NmpClusterKey has not yet been
  500. generated.
  501. ERROR_SUCCESS on success.
  502. Notes:
  503. Acquires and releases NM lock. Since NM lock is
  504. implemented as a critical section, calling thread
  505. is permitted to already hold NM lock.
  506. --*/
  507. {
  508. DWORD status;
  509. NmpAcquireLock();
  510. if (NmpClusterKey == NULL) {
  511. status = ERROR_FILE_NOT_FOUND;
  512. ClRtlLogPrint(LOG_UNUSUAL,
  513. "[NM] The cluster key has not yet been derived.\n"
  514. );
  515. } else{
  516. if (KeyBuffer == NULL || NmpClusterKeyLength > *KeyBufferLength) {
  517. status = ERROR_INSUFFICIENT_BUFFER;
  518. } else {
  519. RtlCopyMemory(KeyBuffer, NmpClusterKey, NmpClusterKeyLength);
  520. status = ERROR_SUCCESS;
  521. }
  522. *KeyBufferLength = NmpClusterKeyLength;
  523. }
  524. NmpReleaseLock();
  525. return(status);
  526. } // NmpGetClusterKey
  527. DWORD
  528. NmpRegenerateClusterKey(
  529. VOID
  530. )
  531. /*++
  532. Routine Description:
  533. Forces regeneration of cluster key.
  534. Must be called during cluster initialization to generate
  535. cluster key the first time.
  536. Notes:
  537. Acquires and releases NM lock.
  538. --*/
  539. {
  540. DWORD status;
  541. BOOLEAN lockAcquired;
  542. PVOID oldKey = NULL;
  543. DWORD oldKeyLength = 0;
  544. PVOID key = NULL;
  545. DWORD keyLength = 0;
  546. PVOID mixingBytes;
  547. DWORD mixingBytesSize;
  548. NmpAcquireLock();
  549. lockAcquired = TRUE;
  550. NmpLockedEnterApi(NmStateOnlinePending);
  551. //
  552. // Form the mixing bytes.
  553. //
  554. if (NmpClusterInstanceId == NULL) {
  555. status = ERROR_INVALID_PARAMETER;
  556. ClRtlLogPrint(LOG_UNUSUAL,
  557. "[NM] Need cluster instance id in order to derive "
  558. "cluster key, status %1!u!.\n",
  559. status
  560. );
  561. goto error_exit;
  562. }
  563. mixingBytesSize = NM_WCSLEN(NmpClusterInstanceId);
  564. mixingBytes = MIDL_user_allocate(mixingBytesSize);
  565. if (mixingBytes == NULL) {
  566. status = ERROR_NOT_ENOUGH_MEMORY;
  567. ClRtlLogPrint(LOG_UNUSUAL,
  568. "[NM] Failed to allocate buffer of size %1!u! "
  569. "for mixing bytes to derive cluster key.\n",
  570. mixingBytesSize
  571. );
  572. goto error_exit;
  573. }
  574. RtlCopyMemory(mixingBytes, NmpClusterInstanceId, mixingBytesSize);
  575. //
  576. // Make a copy of the old key to detect changes.
  577. //
  578. if (NmpClusterKey != NULL) {
  579. CL_ASSERT(NmpClusterKeyLength > 0);
  580. oldKey = MIDL_user_allocate(NmpClusterKeyLength);
  581. if (oldKey == NULL) {
  582. ClRtlLogPrint(LOG_UNUSUAL,
  583. "[NM] Failed to allocate buffer for cluster "
  584. "key copy.\n"
  585. );
  586. status = ERROR_NOT_ENOUGH_MEMORY;
  587. goto error_exit;
  588. }
  589. oldKeyLength = NmpClusterKeyLength;
  590. RtlCopyMemory(oldKey, NmpClusterKey, NmpClusterKeyLength);
  591. }
  592. NmpReleaseLock();
  593. lockAcquired = FALSE;
  594. status = NmpGenerateClusterKey(
  595. mixingBytes,
  596. mixingBytesSize,
  597. &key,
  598. &keyLength
  599. );
  600. if (status != ERROR_SUCCESS) {
  601. ClRtlLogPrint(LOG_UNUSUAL,
  602. "[NM] Failed to generate cluster key, "
  603. "status %1!u!.\n",
  604. status
  605. );
  606. goto error_exit;
  607. }
  608. NmpAcquireLock();
  609. lockAcquired = TRUE;
  610. //
  611. // Make sure another thread didn't beat us in obtaining a key.
  612. // We replace the cluster key with the generated key if it is
  613. // not different from the old key (or somebody set it to NULL).
  614. //
  615. if (NmpClusterKey != NULL &&
  616. (oldKey == NULL ||
  617. NmpClusterKeyLength != oldKeyLength ||
  618. RtlCompareMemory(
  619. NmpClusterKey,
  620. oldKey,
  621. oldKeyLength
  622. ) != oldKeyLength
  623. )
  624. ) {
  625. //
  626. // Keep the current NmpClusterKey.
  627. //
  628. } else {
  629. MIDL_user_free(NmpClusterKey);
  630. NmpClusterKey = key;
  631. NmpClusterKeyLength = keyLength;
  632. key = NULL;
  633. }
  634. error_exit:
  635. if (lockAcquired) {
  636. NmpLockedLeaveApi();
  637. NmpReleaseLock();
  638. } else {
  639. NmpLeaveApi();
  640. }
  641. if (key != NULL) {
  642. MIDL_user_free(key);
  643. key = NULL;
  644. }
  645. if (mixingBytes != NULL) {
  646. MIDL_user_free(mixingBytes);
  647. mixingBytes = NULL;
  648. }
  649. return(status);
  650. } // NmpRegenerateClusterKey
  651. VOID
  652. NmpFreeClusterKey(
  653. VOID
  654. )
  655. /*++
  656. Routine Description:
  657. Called during NmShutdown.
  658. --*/
  659. {
  660. if (NmpClusterKey != NULL) {
  661. MIDL_user_free(NmpClusterKey);
  662. NmpClusterKey = NULL;
  663. NmpClusterKeyLength = 0;
  664. }
  665. return;
  666. } // NmpFreeClusterKey
  667.