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.

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