Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1553 lines
42 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 1992 - 1996
  6. //
  7. // File: notify.cxx
  8. //
  9. // Contents: KDC password change notification code
  10. //
  11. //
  12. // History: 19-Aug-1996 MikeSw Created
  13. //
  14. //------------------------------------------------------------------------
  15. #include "kdcsvr.hxx"
  16. extern "C"
  17. {
  18. #include <dns.h> // DNS_MAX_NAME_LENGTH
  19. #include <ntdsa.h> // CrackSingleName
  20. }
  21. UNICODE_STRING KdcNotifyDnsDomainName = {0};
  22. UNICODE_STRING KdcNotifyDomainName = {0};
  23. //+-------------------------------------------------------------------------
  24. //
  25. // Function: KdcNotifyOpenAccountDomain
  26. //
  27. // Synopsis: Opens the account domain and stores a handle to it.
  28. //
  29. // Effects:
  30. //
  31. // Arguments:
  32. //
  33. // Requires:
  34. //
  35. // Returns:
  36. //
  37. // Notes:
  38. //
  39. //
  40. //--------------------------------------------------------------------------
  41. NTSTATUS
  42. KdcNotifyOpenAccountDomain(
  43. OUT SAMPR_HANDLE * AccountDomainHandle
  44. )
  45. {
  46. NTSTATUS Status;
  47. PLSAPR_POLICY_INFORMATION PolicyInformation = NULL;
  48. SAMPR_HANDLE ServerHandle = NULL;
  49. Status = LsaIQueryInformationPolicyTrusted(
  50. PolicyDnsDomainInformation,
  51. &PolicyInformation
  52. );
  53. if (!NT_SUCCESS(Status))
  54. {
  55. D_DebugLog((DEB_ERROR,"Failed to query information policy: 0x%x\n",Status));
  56. goto Cleanup;
  57. }
  58. Status = KerbDuplicateString(
  59. &KdcNotifyDomainName,
  60. (PUNICODE_STRING) &PolicyInformation->PolicyDnsDomainInfo.Name
  61. );
  62. if (!NT_SUCCESS(Status))
  63. {
  64. goto Cleanup;
  65. }
  66. Status = KerbDuplicateString(
  67. &KdcNotifyDnsDomainName,
  68. (PUNICODE_STRING) &PolicyInformation->PolicyDnsDomainInfo.DnsDomainName
  69. );
  70. if (!NT_SUCCESS(Status))
  71. {
  72. goto Cleanup;
  73. }
  74. Status = RtlUpcaseUnicodeString(
  75. &KdcNotifyDnsDomainName,
  76. &KdcNotifyDnsDomainName,
  77. FALSE // don't allocate
  78. );
  79. if (!NT_SUCCESS(Status))
  80. {
  81. goto Cleanup;
  82. }
  83. //
  84. // Connect to SAM and open the account domain
  85. //
  86. Status = SamIConnect(
  87. NULL, // no server name
  88. &ServerHandle,
  89. 0, // ignore desired access,
  90. TRUE // trusted caller
  91. );
  92. if (!NT_SUCCESS(Status))
  93. {
  94. DebugLog((DEB_ERROR,"Failed to connect to SAM: 0x%x\n",Status));
  95. goto Cleanup;
  96. }
  97. //
  98. // Finally open the account domain.
  99. //
  100. Status = SamrOpenDomain(
  101. ServerHandle,
  102. DOMAIN_ALL_ACCESS,
  103. (PRPC_SID) PolicyInformation->PolicyDnsDomainInfo.Sid,
  104. AccountDomainHandle
  105. );
  106. if (!NT_SUCCESS(Status))
  107. {
  108. DebugLog((DEB_ERROR, "Failed to open account domain: 0x%x\n",Status));
  109. goto Cleanup;
  110. }
  111. Cleanup:
  112. if (PolicyInformation != NULL)
  113. {
  114. LsaIFree_LSAPR_POLICY_INFORMATION(
  115. PolicyDnsDomainInformation,
  116. PolicyInformation
  117. );
  118. }
  119. if (ServerHandle != NULL)
  120. {
  121. SamrCloseHandle(&ServerHandle);
  122. }
  123. return(Status);
  124. }
  125. //+-------------------------------------------------------------------------
  126. //
  127. // Function: KdcBuildPasswordList
  128. //
  129. // Synopsis: Builds a list of passwords for a user that just changed
  130. // their password.
  131. //
  132. // Effects: allocates memory
  133. //
  134. // Arguments: Password - clear or OWF password
  135. // PrincipalName - Name of principal
  136. // MarshallKeys - if TRUE, the keys will be marshalled
  137. // IncludeBuiltinTypes - if TRUE, include MD4 & LM hashes
  138. // PasswordList - Receives new password list
  139. // PasswordListSize - Size of list in bytes.
  140. //
  141. // Requires:
  142. //
  143. // Returns:
  144. //
  145. // Notes:
  146. //
  147. //
  148. //--------------------------------------------------------------------------
  149. NTSTATUS
  150. KdcBuildPasswordList(
  151. IN PUNICODE_STRING Password,
  152. IN PUNICODE_STRING PrincipalName,
  153. IN PUNICODE_STRING DomainName,
  154. IN KERB_ACCOUNT_TYPE AccountType,
  155. IN PKERB_STORED_CREDENTIAL StoredCreds,
  156. IN ULONG StoredCredSize,
  157. IN BOOLEAN MarshallKeys,
  158. IN BOOLEAN IncludeBuiltinTypes,
  159. IN ULONG Flags,
  160. IN KDC_DOMAIN_INFO_DIRECTION Direction,
  161. OUT PKERB_STORED_CREDENTIAL * PasswordList,
  162. OUT PULONG PasswordListSize
  163. )
  164. {
  165. NTSTATUS Status = STATUS_SUCCESS;
  166. ULONG CryptTypes[KERB_MAX_CRYPTO_SYSTEMS];
  167. ULONG CryptCount = 0;
  168. PKERB_STORED_CREDENTIAL Credentials = NULL;
  169. ULONG CredentialSize = 0;
  170. ULONG KerbEncryptionKeyCount = 0;
  171. ULONG KerbKeyDataCount = 0;
  172. PCRYPTO_SYSTEM CryptoSystem;
  173. ULONG Index, CredentialIndex = 0;
  174. PUCHAR Base, KeyBase;
  175. ULONG Offset;
  176. ULONG OldCredCount = 0;
  177. KERB_ENCRYPTION_KEY TempKey;
  178. UNICODE_STRING KeySalt = {0};
  179. UNICODE_STRING EmptySalt = {0};
  180. USHORT OldFlags = 0;
  181. *PasswordList = NULL;
  182. *PasswordListSize = 0;
  183. //
  184. // If we had passed in an OWF, then there is just one password.
  185. //
  186. if ((Flags & KERB_PRIMARY_CRED_OWF_ONLY) != 0)
  187. {
  188. CredentialSize += Password->Length + sizeof(KERB_ENCRYPTION_KEY);
  189. KerbEncryptionKeyCount++;
  190. #ifndef DONT_SUPPORT_OLD_TYPES
  191. CredentialSize += Password->Length + sizeof(KERB_ENCRYPTION_KEY);
  192. KerbEncryptionKeyCount++;
  193. #endif
  194. }
  195. else
  196. {
  197. //
  198. // The salt is the realm name concatenated with the principal name
  199. //
  200. if (AccountType != UnknownAccount)
  201. {
  202. //
  203. // For inbound trust, swap the domain names
  204. //
  205. if ((AccountType == DomainTrustAccount) &&
  206. (Direction == Inbound))
  207. {
  208. if (!KERB_SUCCESS(KerbBuildKeySalt(
  209. PrincipalName,
  210. DomainName,
  211. AccountType,
  212. &KeySalt
  213. )))
  214. {
  215. return(STATUS_INSUFFICIENT_RESOURCES);
  216. }
  217. }
  218. else
  219. {
  220. if (!KERB_SUCCESS(KerbBuildKeySalt(
  221. DomainName,
  222. PrincipalName,
  223. AccountType,
  224. &KeySalt
  225. )))
  226. {
  227. return(STATUS_INSUFFICIENT_RESOURCES);
  228. }
  229. }
  230. }
  231. else
  232. {
  233. KeySalt = *PrincipalName;
  234. }
  235. D_DebugLog((DEB_TRACE,"Building key list with salt %wZ\n",&KeySalt));
  236. //
  237. // For a cleartext password, build a list of encryption types and
  238. // create a key for each one
  239. //
  240. Status = CDBuildIntegrityVect(
  241. &CryptCount,
  242. CryptTypes
  243. );
  244. if (!NT_SUCCESS(Status))
  245. {
  246. goto Cleanup;
  247. }
  248. DsysAssert(CryptCount <= KERB_MAX_CRYPTO_SYSTEMS);
  249. //
  250. // Now find the size of the key for each crypto system
  251. //
  252. for (Index = 0; Index < CryptCount; Index++ )
  253. {
  254. //
  255. // Skip etypes stored as normal passwords
  256. //
  257. if (!IncludeBuiltinTypes &&
  258. ((CryptTypes[Index] == KERB_ETYPE_RC4_LM) ||
  259. (CryptTypes[Index] == KERB_ETYPE_RC4_MD4) ||
  260. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD) ||
  261. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD_EXP) ||
  262. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT) ||
  263. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT_EXP) ||
  264. (CryptTypes[Index] == KERB_ETYPE_NULL)))
  265. {
  266. continue;
  267. }
  268. Status = CDLocateCSystem(
  269. CryptTypes[Index],
  270. &CryptoSystem
  271. );
  272. if (!NT_SUCCESS(Status) || NULL == CryptoSystem)
  273. {
  274. D_DebugLog((DEB_ERROR, "CDLocateCSystem failed for etype: %d\n", CryptTypes[Index]));
  275. continue;
  276. }
  277. CredentialSize += sizeof(KERB_ENCRYPTION_KEY) + CryptoSystem->KeySize;
  278. KerbEncryptionKeyCount++;
  279. }
  280. }
  281. //
  282. // For a cleartext password, build a list of encryption types and
  283. // create a key for each one
  284. //
  285. Status = CDBuildIntegrityVect(
  286. &CryptCount,
  287. CryptTypes
  288. );
  289. if (!NT_SUCCESS(Status))
  290. {
  291. goto Cleanup;
  292. }
  293. DsysAssert(CryptCount <= KERB_MAX_CRYPTO_SYSTEMS);
  294. //
  295. // Add the space for the salt
  296. //
  297. CredentialSize += KeySalt.Length;
  298. //
  299. // Now find the size of the key for each crypto system
  300. //
  301. for (Index = 0; Index < CryptCount; Index++ )
  302. {
  303. //
  304. // Skip etypes stored as normal passwords
  305. //
  306. if (!IncludeBuiltinTypes &&
  307. ((CryptTypes[Index] == KERB_ETYPE_RC4_LM) ||
  308. (CryptTypes[Index] == KERB_ETYPE_RC4_MD4) ||
  309. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD) ||
  310. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD_EXP) ||
  311. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT) ||
  312. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT_EXP) ||
  313. (CryptTypes[Index] == KERB_ETYPE_NULL)))
  314. {
  315. continue;
  316. }
  317. Status = CDLocateCSystem(
  318. CryptTypes[Index],
  319. &CryptoSystem
  320. );
  321. if (!NT_SUCCESS(Status) || NULL == CryptoSystem)
  322. {
  323. D_DebugLog((DEB_ERROR, "CDLocateCSystem failed for etype: %d\n", CryptTypes[Index]));
  324. continue;
  325. }
  326. CredentialSize += sizeof(KERB_KEY_DATA) + CryptoSystem->KeySize;
  327. KerbKeyDataCount++;
  328. }
  329. //
  330. // Add in space for oldcreds
  331. //
  332. if (ARGUMENT_PRESENT(StoredCreds))
  333. {
  334. if ((StoredCreds->Revision == KERB_PRIMARY_CRED_REVISION) &&
  335. (StoredCreds->CredentialCount != 0))
  336. {
  337. OldFlags = StoredCreds->Flags;
  338. for (Index = 0; Index < StoredCreds->CredentialCount ; Index++ )
  339. {
  340. CredentialSize += sizeof(KERB_KEY_DATA) + StoredCreds->Credentials[Index].Key.keyvalue.length +
  341. StoredCreds->Credentials[Index].Salt.Length;
  342. KerbKeyDataCount++;
  343. }
  344. OldCredCount = StoredCreds->CredentialCount;
  345. }
  346. }
  347. //
  348. // Add in the size of the base structure
  349. //
  350. CredentialSize += sizeof(KERB_STORED_CREDENTIAL) - (ANYSIZE_ARRAY * sizeof(KERB_KEY_DATA));
  351. Credentials = (PKERB_STORED_CREDENTIAL) MIDL_user_allocate(CredentialSize);
  352. if (Credentials == NULL)
  353. {
  354. Status = STATUS_INSUFFICIENT_RESOURCES;
  355. goto Cleanup;
  356. }
  357. //
  358. // Fill in the base structure
  359. //
  360. Credentials->Revision = KERB_PRIMARY_CRED_REVISION;
  361. Credentials->Flags = OldFlags | (USHORT) Flags ;
  362. //
  363. // Now fill in the individual keys
  364. //
  365. Base = (PUCHAR) Credentials;
  366. if (MarshallKeys)
  367. {
  368. KeyBase = 0;
  369. }
  370. else
  371. {
  372. KeyBase = Base;
  373. }
  374. Offset = sizeof(KERB_STORED_CREDENTIAL) - (ANYSIZE_ARRAY * sizeof(KERB_KEY_DATA)) +
  375. (KerbEncryptionKeyCount * sizeof(KERB_ENCRYPTION_KEY)) +
  376. (KerbKeyDataCount * sizeof(KERB_KEY_DATA));
  377. //
  378. // Add the default salt
  379. //
  380. Credentials->DefaultSalt.Length =
  381. Credentials->DefaultSalt.MaximumLength = KeySalt.Length;
  382. Credentials->DefaultSalt.Buffer = (LPWSTR) (KeyBase+Offset);
  383. RtlCopyMemory(
  384. Base + Offset,
  385. KeySalt.Buffer,
  386. KeySalt.Length
  387. );
  388. Offset += Credentials->DefaultSalt.Length;
  389. if ((Flags & KERB_PRIMARY_CRED_OWF_ONLY) != 0)
  390. {
  391. RtlCopyMemory(
  392. Base + Offset,
  393. Password->Buffer,
  394. Password->Length
  395. );
  396. if (!KERB_SUCCESS(KerbCreateKeyFromBuffer(
  397. &Credentials->Credentials[CredentialIndex].Key,
  398. Base + Offset,
  399. Password->Length,
  400. KERB_ETYPE_RC4_HMAC_NT
  401. )))
  402. {
  403. Status = STATUS_INSUFFICIENT_RESOURCES;
  404. goto Cleanup;
  405. }
  406. Credentials->Credentials[CredentialIndex].Key.keyvalue.value =
  407. Credentials->Credentials[CredentialIndex].Key.keyvalue.value - Base + KeyBase;
  408. Offset += Password->Length;
  409. CredentialIndex++;
  410. #ifndef DONT_SUPPORT_OLD_TYPES
  411. RtlCopyMemory(
  412. Base + Offset,
  413. Password->Buffer,
  414. Password->Length
  415. );
  416. if (!KERB_SUCCESS(KerbCreateKeyFromBuffer(
  417. &Credentials->Credentials[CredentialIndex].Key,
  418. Base + Offset,
  419. Password->Length,
  420. (ULONG) KERB_ETYPE_RC4_HMAC_OLD
  421. )))
  422. {
  423. Status = STATUS_INSUFFICIENT_RESOURCES;
  424. goto Cleanup;
  425. }
  426. Credentials->Credentials[CredentialIndex].Key.keyvalue.value =
  427. Credentials->Credentials[CredentialIndex].Key.keyvalue.value - Base + KeyBase;
  428. Offset += Password->Length;
  429. CredentialIndex++;
  430. #endif
  431. }
  432. else // assume it's clear
  433. {
  434. //
  435. // Now find the size of the key for each crypto system
  436. //
  437. for (Index = 0; Index < CryptCount; Index++ )
  438. {
  439. if (!IncludeBuiltinTypes &&
  440. ((CryptTypes[Index] == KERB_ETYPE_RC4_LM) ||
  441. (CryptTypes[Index] == KERB_ETYPE_RC4_MD4) ||
  442. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD) ||
  443. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_OLD_EXP) ||
  444. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT) ||
  445. (CryptTypes[Index] == KERB_ETYPE_RC4_HMAC_NT_EXP) ||
  446. (CryptTypes[Index] == KERB_ETYPE_NULL)))
  447. {
  448. continue;
  449. }
  450. if (!KERB_SUCCESS(KerbHashPasswordEx(
  451. Password,
  452. &KeySalt,
  453. CryptTypes[Index],
  454. &TempKey)))
  455. {
  456. //
  457. // It is possible that the password can't be used for every
  458. // encryption scheme, so skip failures
  459. //
  460. D_DebugLog((DEB_WARN, "Failed to hash pasword %wZ with type 0x%x\n",
  461. Password,CryptTypes[Index] ));
  462. continue;
  463. }
  464. #if DBG
  465. CDLocateCSystem(
  466. CryptTypes[Index],
  467. &CryptoSystem
  468. );
  469. DsysAssert(CryptoSystem->KeySize >= TempKey.keyvalue.length);
  470. #endif
  471. Credentials->Credentials[CredentialIndex].Key = TempKey;
  472. Credentials->Credentials[CredentialIndex].Key.keyvalue.value = KeyBase + Offset;
  473. RtlCopyMemory(
  474. Base + Offset,
  475. TempKey.keyvalue.value,
  476. TempKey.keyvalue.length
  477. );
  478. Offset += TempKey.keyvalue.length;
  479. KerbFreeKey(
  480. &TempKey
  481. );
  482. Credentials->Credentials[CredentialIndex].Salt = EmptySalt;
  483. CredentialIndex++;
  484. }
  485. }
  486. Credentials->CredentialCount = (USHORT) CredentialIndex;
  487. //
  488. // Now add in the old creds, if there were any
  489. //
  490. if (OldCredCount != 0)
  491. {
  492. for (Index = 0; Index < OldCredCount ; Index++ )
  493. {
  494. Credentials->Credentials[CredentialIndex] = StoredCreds->Credentials[Index];
  495. Credentials->Credentials[CredentialIndex].Key.keyvalue.value = KeyBase + Offset;
  496. RtlCopyMemory(
  497. Base + Offset,
  498. StoredCreds->Credentials[Index].Key.keyvalue.value + (ULONG_PTR) StoredCreds,
  499. StoredCreds->Credentials[Index].Key.keyvalue.length
  500. );
  501. Offset += StoredCreds->Credentials[Index].Key.keyvalue.length;
  502. //
  503. // Copy the salt
  504. //
  505. if (Credentials->Credentials[CredentialIndex].Salt.Buffer != NULL)
  506. {
  507. Credentials->Credentials[CredentialIndex].Salt.Buffer = (LPWSTR) Base+Offset;
  508. RtlCopyMemory(
  509. Base + Offset,
  510. (PBYTE) StoredCreds->Credentials[Index].Salt.Buffer + (ULONG_PTR) StoredCreds,
  511. StoredCreds->Credentials[Index].Salt.Length
  512. );
  513. Offset += StoredCreds->Credentials[Index].Salt.Length;
  514. }
  515. else
  516. {
  517. Credentials->Credentials[CredentialIndex].Salt = EmptySalt;
  518. }
  519. CredentialIndex++;
  520. }
  521. Credentials->OldCredentialCount = (USHORT) OldCredCount;
  522. }
  523. else
  524. {
  525. Credentials->OldCredentialCount = 0;
  526. }
  527. *PasswordList = Credentials;
  528. *PasswordListSize = CredentialSize;
  529. Credentials = NULL;
  530. Cleanup:
  531. if (Credentials != NULL)
  532. {
  533. MIDL_user_free(Credentials);
  534. }
  535. if (AccountType != UnknownAccount)
  536. {
  537. KerbFreeString(&KeySalt);
  538. }
  539. return(Status);
  540. }
  541. //+-------------------------------------------------------------------------
  542. //
  543. // Function: KdcBuildKeySaltFromUpn
  544. //
  545. // Synopsis: Builds the salt by parsing the UPN, stripping out "@" & "/"
  546. //
  547. // Effects:
  548. //
  549. // Arguments:
  550. //
  551. // Requires:
  552. //
  553. // Returns:
  554. //
  555. // Notes:
  556. //
  557. //
  558. //--------------------------------------------------------------------------
  559. NTSTATUS
  560. KdcBuildKeySaltFromUpn(
  561. IN PUNICODE_STRING Upn,
  562. IN PUNICODE_STRING DomainName,
  563. OUT PUNICODE_STRING Salt
  564. )
  565. {
  566. NTSTATUS Status = STATUS_SUCCESS;
  567. UNICODE_STRING RealUpn;
  568. UNICODE_STRING LocalSalt = {0};
  569. ULONG Index;
  570. //
  571. // If there is an "@" in UPN, strip it out & use the dns domain name
  572. //
  573. RealUpn = *Upn;
  574. // Verify that the length is not 0 SWI
  575. for ( Index = ((RealUpn.Length / sizeof(WCHAR)) - 1); Index-- > 0; )
  576. {
  577. if (RealUpn.Buffer[Index] == L'@')
  578. {
  579. RealUpn.Length = (USHORT) (Index * sizeof(WCHAR));
  580. break;
  581. }
  582. }
  583. //
  584. // Create the salt. It starts off with the domain name & then has the
  585. // UPN without any of the / pieces
  586. //
  587. // ULONG test the length before allocation SWI
  588. LocalSalt.MaximumLength = DomainName->Length + RealUpn.Length;
  589. LocalSalt.Length = 0;
  590. LocalSalt.Buffer = (LPWSTR) MIDL_user_allocate(LocalSalt.MaximumLength);
  591. if (LocalSalt.Buffer == NULL)
  592. {
  593. Status = STATUS_INSUFFICIENT_RESOURCES;
  594. goto Cleanup;
  595. }
  596. RtlCopyMemory(
  597. LocalSalt.Buffer,
  598. DomainName->Buffer,
  599. DomainName->Length
  600. );
  601. LocalSalt.Length = LocalSalt.Length + DomainName->Length;
  602. //
  603. // We have to uppercase the realmname for users
  604. //
  605. (VOID) RtlUpcaseUnicodeString( &LocalSalt,
  606. &LocalSalt,
  607. FALSE);
  608. //
  609. // Add in the real upn but leave out any "/" marks
  610. //
  611. for (Index = 0; Index < RealUpn.Length/sizeof(WCHAR) ; Index++ )
  612. {
  613. if (RealUpn.Buffer[Index] != L'/')
  614. {
  615. LocalSalt.Buffer[LocalSalt.Length / sizeof(WCHAR)] = RealUpn.Buffer[Index];
  616. LocalSalt.Length += sizeof(WCHAR);
  617. }
  618. }
  619. *Salt = LocalSalt;
  620. Cleanup:
  621. return(Status);
  622. }
  623. //+-------------------------------------------------------------------------
  624. //
  625. // Function: PasswordChangeNotify
  626. //
  627. // Synopsis: Notifies KDC of a password change, allowing it to update
  628. // its credentials
  629. //
  630. // Effects: Stores Kerberos credentials on user object
  631. //
  632. // Arguments: UserName - Name of user whose password changed
  633. // RelativeId - RID of changed user
  634. // Passsword - New password of user
  635. //
  636. // Requires:
  637. //
  638. // Returns: STATUS_SUCCESS on success
  639. //
  640. // Notes:
  641. //
  642. //
  643. //--------------------------------------------------------------------------
  644. extern "C"
  645. NTSTATUS
  646. PasswordChangeNotify(
  647. IN PUNICODE_STRING UserName,
  648. IN ULONG RelativeId,
  649. IN PUNICODE_STRING Password
  650. )
  651. {
  652. //
  653. // Password change notify routine in kdcsvc was used to compute the
  654. // "DES" keys for the user upon a password change.
  655. // Subsequently this logic was inlined in samsrv.dll
  656. //
  657. return(STATUS_SUCCESS);
  658. }
  659. extern "C"
  660. NTSTATUS
  661. KdcBuildKerbCredentialsFromPassword(
  662. IN PUNICODE_STRING ClearPassword,
  663. IN PVOID KerbCredentials,
  664. IN ULONG KerbCredentialLength,
  665. IN ULONG UserAccountControl,
  666. IN PUNICODE_STRING UPN,
  667. IN PUNICODE_STRING UserName,
  668. IN PUNICODE_STRING DnsDomainName,
  669. OUT PVOID * NewKerbCredentials,
  670. OUT PULONG NewKerbCredentialLength
  671. )
  672. {
  673. NTSTATUS Status = STATUS_SUCCESS;
  674. KERB_ACCOUNT_TYPE AccountType = UnknownAccount;
  675. UNICODE_STRING KeySalt = {0};
  676. BOOLEAN FreeSalt = FALSE;
  677. PKERB_STORED_CREDENTIAL Cred64 = NULL;
  678. #ifdef _WIN64
  679. PKERB_STORED_CREDENTIAL32 Cred32 = NULL;
  680. ULONG CredLength = KerbCredentialLength;
  681. #endif
  682. //
  683. // Compute the correct account type
  684. //
  685. if ((UserAccountControl &
  686. (USER_WORKSTATION_TRUST_ACCOUNT | USER_SERVER_TRUST_ACCOUNT)) != 0)
  687. {
  688. AccountType = MachineAccount;
  689. KeySalt = *UserName;
  690. }
  691. else if ((UserAccountControl &
  692. (USER_INTERDOMAIN_TRUST_ACCOUNT)) != 0)
  693. {
  694. AccountType = DomainTrustAccount;
  695. KeySalt = *UserName;
  696. }
  697. else
  698. {
  699. if (ARGUMENT_PRESENT(UPN))
  700. {
  701. Status = KdcBuildKeySaltFromUpn(
  702. UPN,
  703. DnsDomainName,
  704. &KeySalt
  705. );
  706. if (!NT_SUCCESS(Status))
  707. {
  708. goto Cleanup;
  709. }
  710. FreeSalt = TRUE;
  711. }
  712. else
  713. {
  714. AccountType = UserAccount;
  715. KeySalt = *UserName;
  716. }
  717. }
  718. #ifdef _WIN64
  719. Status = KdcUnpack32BitStoredCredential(
  720. (PKERB_STORED_CREDENTIAL32) KerbCredentials,
  721. &Cred64,
  722. &CredLength
  723. );
  724. if (!NT_SUCCESS(Status))
  725. {
  726. goto Cleanup;
  727. }
  728. KerbCredentials = (PVOID) Cred64;
  729. KerbCredentialLength = CredLength;
  730. #endif
  731. //
  732. // Compute the kerb credentials
  733. //
  734. if ((ClearPassword != NULL))
  735. {
  736. UNICODE_STRING UpcaseDomainName = {0};
  737. Status = RtlUpcaseUnicodeString(
  738. &UpcaseDomainName,
  739. DnsDomainName,
  740. TRUE
  741. );
  742. if (NT_SUCCESS(Status))
  743. {
  744. Status = KdcBuildPasswordList(
  745. ClearPassword,
  746. &KeySalt,
  747. &UpcaseDomainName,
  748. AccountType,
  749. (PKERB_STORED_CREDENTIAL )KerbCredentials,
  750. KerbCredentialLength,
  751. TRUE, // marshall
  752. FALSE, // don't include builtins
  753. 0, // no flags
  754. Unknown,
  755. (PKERB_STORED_CREDENTIAL *) NewKerbCredentials,
  756. NewKerbCredentialLength
  757. );
  758. RtlFreeUnicodeString(&UpcaseDomainName);
  759. if (!NT_SUCCESS(Status))
  760. {
  761. goto Cleanup;
  762. }
  763. }
  764. }
  765. #ifdef _WIN64
  766. // for 64 - 32 bit compat, we pack the struct in 32bit compliant form
  767. Status = KdcPack32BitStoredCredential(
  768. (PKERB_STORED_CREDENTIAL)(*NewKerbCredentials),
  769. &Cred32,
  770. NewKerbCredentialLength
  771. );
  772. if (!NT_SUCCESS(Status))
  773. {
  774. goto Cleanup;
  775. }
  776. if ((*NewKerbCredentials) != NULL)
  777. {
  778. MIDL_user_free(*NewKerbCredentials);
  779. *NewKerbCredentials = Cred32;
  780. }
  781. #endif
  782. Cleanup:
  783. if (FreeSalt)
  784. {
  785. KerbFreeString(&KeySalt);
  786. }
  787. if (Cred64 != NULL)
  788. {
  789. MIDL_user_free(Cred64);
  790. }
  791. return(Status);
  792. }
  793. extern "C"
  794. VOID
  795. KdcFreeCredentials(
  796. IN PVOID Credentials
  797. )
  798. {
  799. MIDL_user_free(Credentials);
  800. }
  801. //+-------------------------------------------------------------------------
  802. //
  803. // Function: InitializeChangeNotify
  804. //
  805. // Synopsis: KDC code for initializing password change notification
  806. // code.
  807. //
  808. // Effects:
  809. //
  810. // Arguments:
  811. //
  812. // Requires:
  813. //
  814. // Returns:
  815. //
  816. // Notes:
  817. //
  818. //
  819. //--------------------------------------------------------------------------
  820. extern "C"
  821. BOOLEAN
  822. InitializeChangeNotify(
  823. )
  824. {
  825. return(TRUE);
  826. }
  827. //+-------------------------------------------------------------------------
  828. //
  829. // Function: KdcTimeHasElapsed
  830. //
  831. // Synopsis: Returns TRUE if the specified amount of time has
  832. // elapsed since the specified start time
  833. //
  834. // Effects:
  835. //
  836. // Arguments:
  837. //
  838. // Requires:
  839. //
  840. // Returns:
  841. //
  842. // Notes:
  843. //
  844. //
  845. //--------------------------------------------------------------------------
  846. BOOLEAN
  847. KdcTimeHasElapsed(
  848. IN LARGE_INTEGER StartTime,
  849. IN PLARGE_INTEGER Delta
  850. )
  851. {
  852. LARGE_INTEGER CurrentTime;
  853. LARGE_INTEGER ElapsedTime;
  854. //
  855. // Check the password expiration time.
  856. //
  857. NtQuerySystemTime(&CurrentTime);
  858. ElapsedTime.QuadPart = CurrentTime.QuadPart - StartTime.QuadPart;
  859. //
  860. // If the window hasn't elapsed, we are done.
  861. //
  862. if ((ElapsedTime.QuadPart > 0) && (ElapsedTime.QuadPart < Delta->QuadPart))
  863. {
  864. return(FALSE);
  865. }
  866. return(TRUE);
  867. }
  868. //+-------------------------------------------------------------------------
  869. //
  870. // Function: KdcUpdateKrbtgtPassword
  871. //
  872. // Synopsis:
  873. //
  874. // Effects:
  875. //
  876. // Arguments:
  877. //
  878. // Requires:
  879. //
  880. // Returns:
  881. //
  882. // Notes:
  883. //
  884. //
  885. //--------------------------------------------------------------------------
  886. extern "C"
  887. BOOLEAN
  888. KdcUpdateKrbtgtPassword(
  889. IN PUNICODE_STRING DnsDomainName,
  890. IN PLARGE_INTEGER MaxPasswordAge
  891. )
  892. {
  893. NTSTATUS Status = STATUS_SUCCESS;
  894. WCHAR PasswordBuffer[LM20_PWLEN];
  895. UNICODE_STRING PasswordString;
  896. ULONG Index;
  897. BOOLEAN Result = FALSE;
  898. if (KdcState != Running)
  899. {
  900. goto Cleanup;
  901. }
  902. //
  903. // Check the password expiration time.
  904. //
  905. if (!KdcTimeHasElapsed(
  906. SecData.KrbtgtPasswordLastSet(),
  907. MaxPasswordAge
  908. ))
  909. {
  910. goto Cleanup;
  911. }
  912. //
  913. // Build a random password
  914. //
  915. if (!CDGenerateRandomBits(
  916. (PBYTE) PasswordBuffer,
  917. sizeof(PasswordBuffer)
  918. ))
  919. {
  920. Status = STATUS_INTERNAL_ERROR;
  921. goto Cleanup;
  922. }
  923. //
  924. // Make sure there are no zero characters
  925. //
  926. for (Index = 0; Index < LM20_PWLEN ; Index++ )
  927. {
  928. if (PasswordBuffer[Index] == 0)
  929. {
  930. PasswordBuffer[Index] = (WCHAR) Index;
  931. }
  932. }
  933. PasswordString.Length = sizeof(PasswordBuffer);
  934. PasswordString.MaximumLength = PasswordString.Length;
  935. PasswordString.Buffer = PasswordBuffer;
  936. Status = SamIChangePasswordForeignUser(
  937. SecData.KdcServiceName(),
  938. &PasswordString,
  939. NULL,
  940. 0 // no desired access
  941. );
  942. if (!NT_SUCCESS(Status))
  943. {
  944. D_DebugLog((DEB_ERROR,"Failed to set KRBTGT password: 0x%x\n", Status));
  945. Result = FALSE;
  946. goto Cleanup;
  947. }
  948. ReportServiceEvent(
  949. EVENTLOG_SUCCESS,
  950. KDCEVENT_KRBTGT_PASSWORD_CHANGED,
  951. 0, // no data
  952. NULL, // no data
  953. 0, // no strings
  954. NULL // no strings
  955. );
  956. Result = TRUE;
  957. Cleanup:
  958. if (!Result && !NT_SUCCESS(Status))
  959. {
  960. ReportServiceEvent(
  961. EVENTLOG_ERROR_TYPE,
  962. KDCEVENT_KRBTGT_PASSWORD_CHANGE_FAILED,
  963. sizeof(NTSTATUS),
  964. &Status,
  965. 0, // no strings
  966. NULL // no strings
  967. );
  968. }
  969. return(Result);
  970. }
  971. //+-------------------------------------------------------------------------
  972. //
  973. // Function: CredentialUpdateNotify
  974. //
  975. // Synopsis: This routine is called from SAMSRV in order to obtain
  976. // new kerberos credentials to be stored as supplemental
  977. // credentials when ever a user's password is set/changed.
  978. //
  979. // Effects: no global effect.
  980. //
  981. // Arguments:
  982. //
  983. // IN ClearPassword -- the clear text password
  984. // IN OldCredentials -- the previous kerberos credentials
  985. // IN OldCredentialsSize -- size of OldCredentials
  986. // IN UserAccountControl -- info about the user
  987. // IN UPN -- user principal name of the account
  988. // IN UserName -- the SAM account name of the account
  989. // IN DnsDomainName -- DNS domain name of the account
  990. // OUT NewCredentials -- space allocated for SAM containing
  991. // the credentials based on the input parameters
  992. // to be freed by CredentialUpdateFree
  993. // OUT NewCredentialSize -- size of NewCredentials
  994. //
  995. //
  996. // Requires: no global requirements
  997. //
  998. // Returns: STATUS_SUCCESS, or resource error
  999. //
  1000. // Notes: KDCSVC.DLL needs to be registered (in the registry) as a
  1001. // package that SAM calls out to in order for this routine
  1002. // to be involked.
  1003. //
  1004. //
  1005. //--------------------------------------------------------------------------
  1006. NTSTATUS
  1007. CredentialUpdateNotify (
  1008. IN PUNICODE_STRING ClearPassword,
  1009. IN PVOID OldCredentials,
  1010. IN ULONG OldCredentialsSize,
  1011. IN ULONG UserAccountControl,
  1012. IN PUNICODE_STRING UPN,
  1013. IN PUNICODE_STRING UserName,
  1014. IN PUNICODE_STRING NetbiosDomainName,
  1015. IN PUNICODE_STRING DnsDomainName,
  1016. OUT PVOID *NewCredentials,
  1017. OUT ULONG *NewCredentialsSize
  1018. )
  1019. {
  1020. UNREFERENCED_PARAMETER( NetbiosDomainName );
  1021. return KdcBuildKerbCredentialsFromPassword(ClearPassword,
  1022. OldCredentials,
  1023. OldCredentialsSize,
  1024. UserAccountControl,
  1025. UPN,
  1026. UserName,
  1027. DnsDomainName,
  1028. NewCredentials,
  1029. NewCredentialsSize);
  1030. }
  1031. VOID
  1032. CredentialUpdateFree(
  1033. PVOID p
  1034. )
  1035. //
  1036. // Free's the memory allocated by CredentialUpdateNotify
  1037. //
  1038. {
  1039. if (p) {
  1040. KdcFreeCredentials(p);
  1041. }
  1042. }
  1043. //+-------------------------------------------------------------------------
  1044. //
  1045. // Function: CredentialUpdateRegister
  1046. //
  1047. // Synopsis: This routine is called from SAMSRV in order to obtain
  1048. // the name of the supplemental credentials pass into this package
  1049. // when a password is changed or set.
  1050. //
  1051. // Effects: no global effect.
  1052. //
  1053. // Arguments:
  1054. //
  1055. // OUT CredentialName -- the name of credential tag in the supplemental
  1056. // credentials. Note this memory is never freed
  1057. // by SAM, but must remain valid for the lifetime
  1058. // of the process.
  1059. //
  1060. // Requires: no global requirements
  1061. //
  1062. // Returns: TRUE
  1063. //
  1064. // Notes: KDCSVC.DLL needs to be registered (in the registry) as a
  1065. // package that SAM calls out to in order for this routine
  1066. // to be involked.
  1067. //
  1068. //
  1069. //--------------------------------------------------------------------------
  1070. BOOLEAN
  1071. CredentialUpdateRegister(
  1072. OUT UNICODE_STRING *CredentialName
  1073. )
  1074. {
  1075. ASSERT(CredentialName);
  1076. RtlInitUnicodeString(CredentialName, MICROSOFT_KERBEROS_NAME_W);
  1077. return TRUE;
  1078. }
  1079. //
  1080. // This compile-time test verifies that CredentialUpdateNotify and
  1081. // CredentialUpdateRegister have the correct signature
  1082. //
  1083. #if DBG
  1084. PSAM_CREDENTIAL_UPDATE_NOTIFY_ROUTINE _TestCredentialUpdateNotify
  1085. = CredentialUpdateNotify;
  1086. PSAM_CREDENTIAL_UPDATE_FREE_ROUTINE _TestCredentialFreeRegister
  1087. = CredentialUpdateFree;
  1088. PSAM_CREDENTIAL_UPDATE_REGISTER_ROUTINE _TestCredentialUpdateRegister
  1089. = CredentialUpdateRegister;
  1090. #endif
  1091. #ifdef _WIN64
  1092. //
  1093. // Routines for packing and unpacking KERB_STORED_CREDENTIAL from DS
  1094. //
  1095. //+-------------------------------------------------------------------------
  1096. //
  1097. // Function: KdcUnpack32BitStoredCredential
  1098. //
  1099. // Synopsis: This function converts a 32 bit KERB_STORED_CREDENTIAL (read
  1100. // from DS, likely) to a 64 bit KERB_STORED_CREDENTIAL
  1101. //
  1102. // Effects:
  1103. //
  1104. // Arguments:
  1105. //
  1106. // Requires:
  1107. //
  1108. // Returns:
  1109. //
  1110. // Notes:
  1111. //
  1112. //
  1113. //--------------------------------------------------------------------------
  1114. NTSTATUS
  1115. KdcUnpack32BitStoredCredential(
  1116. IN PKERB_STORED_CREDENTIAL32 Cred32,
  1117. IN OUT PKERB_STORED_CREDENTIAL * ppCred64,
  1118. IN OUT PULONG pCredLength
  1119. )
  1120. {
  1121. PKERB_STORED_CREDENTIAL Cred64 = NULL;
  1122. NTSTATUS Status = STATUS_SUCCESS;
  1123. ULONG CredSize = sizeof(KERB_STORED_CREDENTIAL);
  1124. ULONG CredCount = 0, Cred = 0, Offset = 0;
  1125. PCHAR Where, Base;
  1126. CHAR UNALIGNED * From;
  1127. *pCredLength = 0;
  1128. *ppCred64 = NULL;
  1129. if (NULL == Cred32)
  1130. {
  1131. return STATUS_SUCCESS;
  1132. }
  1133. // Calculate Allocation size
  1134. CredSize += ROUND_UP_COUNT(Cred32->DefaultSalt.MaximumLength, ALIGN_LPTSTR);
  1135. CredSize += ((Cred32->CredentialCount + Cred32->OldCredentialCount) * sizeof(KERB_KEY_DATA));
  1136. for (CredCount = 0; CredCount < (ULONG)(Cred32->CredentialCount + Cred32->OldCredentialCount); CredCount++)
  1137. {
  1138. CredSize += ROUND_UP_COUNT(Cred32->Credentials[CredCount].Key.keyvaluelength, ALIGN_LPTSTR);
  1139. CredSize += ROUND_UP_COUNT(Cred32->Credentials[CredCount].Salt.MaximumLength, ALIGN_LPTSTR);
  1140. }
  1141. Cred64 = (PKERB_STORED_CREDENTIAL) MIDL_user_allocate(CredSize);
  1142. if (NULL == Cred64)
  1143. {
  1144. return STATUS_INSUFFICIENT_RESOURCES;
  1145. }
  1146. // copy over data, remember, buffers packed in self-relative format
  1147. Cred64->CredentialCount = Cred32->CredentialCount;
  1148. Cred64->OldCredentialCount = Cred32->OldCredentialCount;
  1149. Cred64->DefaultSalt.Length = Cred32->DefaultSalt.Length;
  1150. Cred64->DefaultSalt.MaximumLength = Cred32->DefaultSalt.MaximumLength;
  1151. Cred64->Flags = Cred32->Flags;
  1152. Cred64->Revision = Cred32->Revision;
  1153. Base = (PCHAR)Cred64;
  1154. From = (CHAR UNALIGNED *) RtlOffsetToPointer(Cred32,Cred32->DefaultSalt.Buffer);
  1155. // Note: 1 KERB_KEY_DATA struct is already calculated in
  1156. // the sizeof(KERB_STORED_CREDENTIAL)
  1157. Offset = sizeof(KERB_STORED_CREDENTIAL) + ((CredCount) * sizeof(KERB_KEY_DATA));
  1158. Where = RtlOffsetToPointer(Cred64, Offset);
  1159. Cred64->DefaultSalt.Buffer = (PWSTR) (ULONG_PTR) Offset;
  1160. RtlCopyMemory(
  1161. Where,
  1162. From,
  1163. Cred32->DefaultSalt.Length
  1164. );
  1165. Where += ROUND_UP_COUNT(Cred64->DefaultSalt.Length, ALIGN_LPTSTR);
  1166. // copy credentials
  1167. for (Cred = 0; Cred < CredCount; Cred++)
  1168. {
  1169. Cred64->Credentials[Cred].Salt.Length = Cred32->Credentials[Cred].Salt.Length;
  1170. Cred64->Credentials[Cred].Salt.MaximumLength = Cred32->Credentials[Cred].Salt.MaximumLength;
  1171. From = (CHAR UNALIGNED *) RtlOffsetToPointer(Cred32, Cred32->Credentials[Cred].Salt.Buffer);
  1172. if (Cred32->Credentials[Cred].Salt.Length != 0)
  1173. {
  1174. Cred64->Credentials[Cred].Salt.Buffer = (PWSTR) (ULONG_PTR) RtlPointerToOffset(Base,Where);
  1175. }
  1176. RtlCopyMemory(
  1177. Where,
  1178. From,
  1179. Cred32->Credentials[Cred].Salt.Length
  1180. );
  1181. Where += ROUND_UP_COUNT(Cred64->Credentials[Cred].Salt.Length, ALIGN_LPTSTR);
  1182. Cred64->Credentials[Cred].Key.keytype = Cred32->Credentials[Cred].Key.keytype;
  1183. Cred64->Credentials[Cred].Key.keyvalue.length = Cred32->Credentials[Cred].Key.keyvaluelength;
  1184. From = RtlOffsetToPointer(Cred32,Cred32->Credentials[Cred].Key.keyvaluevalue);
  1185. Cred64->Credentials[Cred].Key.keyvalue.value = (PUCHAR) (ULONG_PTR) RtlPointerToOffset(Base,Where);
  1186. RtlCopyMemory(
  1187. Where,
  1188. From,
  1189. Cred32->Credentials[Cred].Key.keyvaluelength
  1190. );
  1191. Where += ROUND_UP_COUNT(Cred32->Credentials[Cred].Key.keyvaluelength, ALIGN_LPTSTR);
  1192. }
  1193. // TBD: Validation code ?
  1194. *ppCred64 = Cred64;
  1195. *pCredLength = CredSize;
  1196. return Status;
  1197. }
  1198. //+-------------------------------------------------------------------------
  1199. //
  1200. // Function: KdcPack32BitStoredCredential
  1201. //
  1202. // Synopsis: This function converts a 64 bit KERB_STORED_CREDENTIAL
  1203. // to a 32 bit KERB_STORED_CREDENTIAL
  1204. //
  1205. // Effects:
  1206. //
  1207. // Arguments:
  1208. //
  1209. // Requires:
  1210. //
  1211. // Returns:
  1212. //
  1213. // Notes: Free the return value using MIDL_user_free()
  1214. //
  1215. //
  1216. //--------------------------------------------------------------------------
  1217. NTSTATUS
  1218. KdcPack32BitStoredCredential(
  1219. IN PKERB_STORED_CREDENTIAL Cred64,
  1220. OUT PKERB_STORED_CREDENTIAL32 * ppCred32,
  1221. OUT PULONG pCredSize
  1222. )
  1223. {
  1224. ULONG Offset, CredSize = sizeof(KERB_STORED_CREDENTIAL32);
  1225. ULONG CredCount, Cred;
  1226. PKERB_STORED_CREDENTIAL32 Cred32 = NULL;
  1227. PCHAR Where, From, Base;
  1228. *ppCred32 = NULL;
  1229. *pCredSize = 0;
  1230. if (Cred64 == NULL)
  1231. {
  1232. return STATUS_SUCCESS;
  1233. }
  1234. // Get the expected size of the resultant blob
  1235. CredSize += ((Cred64->CredentialCount + Cred64->OldCredentialCount) *
  1236. KERB_KEY_DATA32_SIZE);
  1237. CredSize += Cred64->DefaultSalt.MaximumLength;
  1238. for (CredCount = 0;
  1239. CredCount < (ULONG) (Cred64->CredentialCount+Cred64->OldCredentialCount);
  1240. CredCount++)
  1241. {
  1242. CredSize += Cred64->Credentials[CredCount].Salt.MaximumLength;
  1243. CredSize += Cred64->Credentials[CredCount].Key.keyvalue.length;
  1244. }
  1245. Cred32 = (PKERB_STORED_CREDENTIAL32) MIDL_user_allocate(CredSize);
  1246. if (NULL == Cred32)
  1247. {
  1248. return STATUS_INSUFFICIENT_RESOURCES;
  1249. }
  1250. Base = (PCHAR) Cred32;
  1251. // Copy over USHORTS
  1252. Cred32->Revision = Cred64->Revision;
  1253. Cred32->Flags = Cred64->Flags;
  1254. Cred32->CredentialCount = Cred64->CredentialCount;
  1255. Cred32->OldCredentialCount = Cred64->OldCredentialCount;
  1256. // Copy over salt
  1257. Cred32->DefaultSalt.Length = Cred64->DefaultSalt.Length;
  1258. Cred32->DefaultSalt.MaximumLength = Cred64->DefaultSalt.MaximumLength;
  1259. Offset = KERB_STORED_CREDENTIAL32_SIZE + ((CredCount+1) * KERB_KEY_DATA32_SIZE);
  1260. Where = RtlOffsetToPointer(Base,Offset);
  1261. From = RtlOffsetToPointer(Cred64, Cred64->DefaultSalt.Buffer);
  1262. Cred32->DefaultSalt.Buffer = RtlPointerToOffset(Base,Where);
  1263. RtlCopyMemory(
  1264. Where,
  1265. From,
  1266. Cred64->DefaultSalt.Length
  1267. );
  1268. Where += Cred64->DefaultSalt.Length;
  1269. // Copy over creds (KERB_KEY_DATA)
  1270. for (Cred = 0; Cred < CredCount;Cred++)
  1271. {
  1272. Cred32->Credentials[Cred].Salt.Length = Cred64->Credentials[Cred].Salt.Length;
  1273. Cred32->Credentials[Cred].Salt.MaximumLength = Cred64->Credentials[Cred].Salt.MaximumLength;
  1274. From = RtlOffsetToPointer(Cred64, Cred64->Credentials[Cred].Salt.Buffer);
  1275. // Only add in buffer pointer if there's data to copy.
  1276. if (Cred32->Credentials[Cred].Salt.Length != 0)
  1277. {
  1278. Cred32->Credentials[Cred].Salt.Buffer = RtlPointerToOffset(Base,Where);
  1279. }
  1280. RtlCopyMemory(
  1281. Where,
  1282. From,
  1283. Cred64->Credentials[Cred].Salt.Length
  1284. );
  1285. Where += Cred64->Credentials[Cred].Salt.Length;
  1286. // Keys
  1287. Cred32->Credentials[Cred].Key.keytype = Cred64->Credentials[Cred].Key.keytype ;
  1288. Cred32->Credentials[Cred].Key.keyvaluelength = Cred64->Credentials[Cred].Key.keyvalue.length;
  1289. From = RtlOffsetToPointer(Cred64, Cred64->Credentials[Cred].Key.keyvalue.value);
  1290. RtlCopyMemory(
  1291. Where,
  1292. From,
  1293. Cred64->Credentials[Cred].Key.keyvalue.length
  1294. );
  1295. Cred32->Credentials[Cred].Key.keyvaluevalue = RtlPointerToOffset(Base,Where);
  1296. Where += Cred64->Credentials[Cred].Key.keyvalue.length;
  1297. }
  1298. *ppCred32 = Cred32;
  1299. *pCredSize = CredSize;
  1300. return STATUS_SUCCESS;
  1301. }
  1302. #endif