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.

663 lines
22 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 2000
  6. //
  7. // File: notify.cxx
  8. //
  9. // Contents: Credential functions:
  10. //
  11. //
  12. // History: KDamour 29Mar01 Created
  13. //
  14. //------------------------------------------------------------------------
  15. #include <stdio.h>
  16. #include "global.h"
  17. //
  18. // Precomputed digest hash functions
  19. //
  20. // This is the ordered list of digest hash functions contained in the supplemental (primary)
  21. // credential for the user in the DS on the DC. The order is imporant to preserve and new
  22. // versions of the hash functions can be appended to the list the the DigestSelectHash()
  23. // function updated
  24. //
  25. // H(davemo:redmond:MyPassword) - I think this form is used for some sasl implementations
  26. // and is the default way our client side packages the info when you use the auth
  27. // identity structure.
  28. //
  29. // H(redmond\davemo:corp.microsoft.com:MyPassword) - this form will handle backwards compatibility
  30. // with older IE clients. Currently a Digest IE client will type in a username that is the
  31. // netbios domain\user form and the client will return the realm hint. The realm hint will
  32. // be provided by new Digest (IIS) servers and determined by a call to
  33. // DsRoleGetPrimaryDomainInformation to retrieve DomainForestName.
  34. //
  35. // H([email protected]:corp.microsoft.com:MyPassword) - We want to eventually move
  36. // everyone to UPNs and this gives us forward compatiblity. The realm value comes from the
  37. // same method as above.
  38. //
  39. // Each format will have argument formatted, uppercase, lowercase formats.
  40. //+-------------------------------------------------------------------------
  41. //
  42. // Function: CredentialUpdateNotify
  43. //
  44. // Synopsis: This routine is called from LSA in order to obtain
  45. // new Digest Hash credentials to be stored as supplemental
  46. // credentials when ever a user's password is set/changed.
  47. // These precalculated hashes can be used instead of turning
  48. // on Reversible Encryption on the DC
  49. //
  50. // Effects: no global effect.
  51. //
  52. // Arguments:
  53. //
  54. // IN ClearPassword -- the clear text password
  55. // IN OldCredentials -- the previous digest credentials
  56. // IN OldCredentialsSize -- size of OldCredentials
  57. // IN UserAccountControl -- info about the user
  58. // IN UPN -- user principal name of the account (Optional)
  59. // IN UserName -- the SAM account name of the account
  60. // IN DnsDomainName -- DNS domain name of the account
  61. // OUT NewCredentials -- space allocated for SAM containing
  62. // the credentials based on the input parameters
  63. // to be freed by CredentialUpdateFree
  64. // OUT NewCredentialSize -- size of NewCredentials
  65. //
  66. //
  67. // Requires: no global requirements
  68. //
  69. // Returns: STATUS_SUCCESS, or resource error
  70. //
  71. // Notes: WDigest.DLL needs to be registered (in the registry) as a
  72. // package that SAM calls out to in order for this routine
  73. // to be involked.
  74. // We are called when the password changes. This routine is not
  75. // called when the username, UPN, or DNSDomainName changes. It is
  76. // a known issue and noted that the users must change their passwords
  77. // to populate a updated hash after a domainname change.
  78. // Need to set key
  79. // \Registry\Machine\System\CurrentControlSet\Control\LSA Notification Packages
  80. //
  81. //
  82. //--------------------------------------------------------------------------
  83. NTSTATUS
  84. CredentialUpdateNotify (
  85. IN PUNICODE_STRING ClearPassword,
  86. IN PVOID OldCredentials,
  87. IN ULONG OldCredentialsSize,
  88. IN ULONG UserAccountControl,
  89. IN PUNICODE_STRING UPN,
  90. IN PUNICODE_STRING UserName,
  91. IN PUNICODE_STRING NetbiosDomainName,
  92. IN PUNICODE_STRING DnsDomainName,
  93. OUT PVOID *NewCredentials,
  94. OUT ULONG *NewCredentialsSize
  95. )
  96. {
  97. // Structure will be composed of a series of binary hash values
  98. // Each hash is a binary version of a MD5 hash of H(username:realm:password)
  99. // Each hash is MD5_HASH_BYTESIZE (16 bytes) in length. The Hex version of
  100. // that is 32 bytes in length.
  101. // The supplimental creds will have the following structure
  102. // All versions will have the similar start of 16 byte header - version number
  103. // Version 1 supplimental creds looks like
  104. // Header (version number as ASCII string <sp> number of pre-calculated hashes) i.e "1 6\0\0...\0"
  105. // H(username, short domain name, password) (16 bytes)
  106. // H(UPN, NULL string, password)
  107. NTSTATUS Status = STATUS_SUCCESS;
  108. NET_API_STATUS NetStatus = NERR_Success;
  109. PCHAR pWhere = NULL;
  110. PCHAR pcSuppCreds = NULL;
  111. ULONG ulSuppCredSize = 0;
  112. USHORT usLength = 0;
  113. USHORT ucTotalByteCount = 0;
  114. DWORD dwRC = 0;
  115. USHORT usNumWChars = 0;
  116. PWCHAR pwBS = L"\\";
  117. UNICODE_STRING ustrFlatDomainName = {0};;
  118. UNICODE_STRING ustrNetBios = {0};
  119. DebugLog((DEB_TRACE_FUNC, "CredentialUpdateNotify: Entering\n"));
  120. DebugLog((DEB_TRACE, "CredentialUpdateNotify: UPN (%wZ), Username (%wZ), DNSDomainName (%wZ)\n",
  121. UPN, UserName, DnsDomainName));
  122. ASSERT(NewCredentials);
  123. ASSERT(NewCredentialsSize);
  124. *NewCredentials = NULL;
  125. *NewCredentialsSize = NULL;
  126. ulSuppCredSize = (NUMPRECALC_HEADERS+ 1) * MD5_HASH_BYTESIZE; // 1 for the header
  127. pcSuppCreds = (PCHAR)DigestAllocateMemory(ulSuppCredSize);
  128. if (!pcSuppCreds)
  129. {
  130. Status = STATUS_INSUFFICIENT_RESOURCES;
  131. DebugLog((DEB_ERROR, "CredentialUpdateNotify: Error from Digest Allocate is 0x%lx\n", Status));
  132. goto CleanUp;
  133. }
  134. usNumWChars = (NetbiosDomainName->Length + sizeof(WCHAR)) / sizeof(WCHAR);
  135. Status = UnicodeStringAllocate(&ustrFlatDomainName, usNumWChars);
  136. if (!NT_SUCCESS (Status))
  137. {
  138. DebugLog((DEB_ERROR, "CredentialUpdateNotify: Copy Domain error 0x%x\n", Status));
  139. goto CleanUp;
  140. }
  141. RtlCopyUnicodeString(&ustrFlatDomainName, NetbiosDomainName);
  142. Status = RtlUpcaseUnicodeString(&ustrFlatDomainName, &ustrFlatDomainName, FALSE);
  143. if (!NT_SUCCESS (Status))
  144. {
  145. DebugLog((DEB_ERROR, "CredentialUpdateNotify: Upcase failed error 0x%x\n", Status));
  146. goto CleanUp;
  147. }
  148. DebugLog((DEB_TRACE, "CredentialUpdateNotify: Flat DomainName (%wZ)\n", &ustrFlatDomainName));
  149. // Hash 0 Header information
  150. pWhere = pcSuppCreds;
  151. sprintf(pWhere, "%d %d", SUPPCREDS_VERSION, NUMPRECALC_HEADERS);
  152. pWhere += MD5_HASH_BYTESIZE;
  153. ucTotalByteCount += MD5_HASH_BYTESIZE;
  154. // Now write out the pre-calculated hashes
  155. // IMPORTANT to make sure that NUMPRECALC_HEADERS is updated if new hashes added !!!!!!
  156. // Hash 1 username:FlatDomainName:password
  157. usLength = MD5_HASH_BYTESIZE * 3;
  158. Status = PrecalcForms(UserName, &ustrFlatDomainName, ClearPassword, pWhere, &usLength);
  159. if (!NT_SUCCESS (Status))
  160. {
  161. DebugLog((DEB_ERROR, "CredentialUpdateNotify: PrecalcDigestHash error 0x%x\n", Status));
  162. goto CleanUp;
  163. }
  164. pWhere += usLength;
  165. ucTotalByteCount += usLength;
  166. // Hash 2 UPN::password
  167. usLength = MD5_HASH_BYTESIZE * 3;;
  168. Status = PrecalcForms(UPN, NULL, ClearPassword, pWhere, &usLength);
  169. if (!NT_SUCCESS (Status))
  170. {
  171. DebugLog((DEB_ERROR, "CredentialUpdateNotify: PrecalcDigestHash error 0x%x\n", Status));
  172. goto CleanUp;
  173. }
  174. pWhere += usLength;
  175. ucTotalByteCount += usLength;
  176. // Hash 3 NetBIOS::password
  177. // NetBIOS name flatdomain\username
  178. usNumWChars = (ustrFlatDomainName.Length + UserName->Length + sizeof(WCHAR)) / sizeof(WCHAR);
  179. Status = UnicodeStringAllocate(&ustrNetBios, usNumWChars);
  180. if (!NT_SUCCESS (Status))
  181. {
  182. DebugLog((DEB_ERROR, "CredentialUpdateNotify: Copy Domain error 0x%x\n", Status));
  183. goto CleanUp;
  184. }
  185. RtlCopyUnicodeString(&ustrNetBios, &ustrFlatDomainName);
  186. Status = RtlAppendUnicodeToString(&ustrNetBios, pwBS);
  187. if (!NT_SUCCESS (Status))
  188. {
  189. DebugLog((DEB_ERROR, "CredentialUpdateNotify: Append Separator error 0x%x\n", Status));
  190. goto CleanUp;
  191. }
  192. Status = RtlAppendUnicodeStringToString(&ustrNetBios, UserName);
  193. if (!NT_SUCCESS (Status))
  194. {
  195. DebugLog((DEB_ERROR, "CredentialUpdateNotify: Append username error 0x%x\n", Status));
  196. goto CleanUp;
  197. }
  198. DebugLog((DEB_TRACE, "CredentialUpdateNotify: NetBIOS name %wZ\n", &ustrNetBios));
  199. usLength = MD5_HASH_BYTESIZE * 3;;
  200. Status = PrecalcForms(&ustrNetBios, NULL, ClearPassword, pWhere, &usLength);
  201. if (!NT_SUCCESS (Status))
  202. {
  203. DebugLog((DEB_ERROR, "CredentialUpdateNotify: PrecalcDigestHash error 0x%x\n", Status));
  204. goto CleanUp;
  205. }
  206. ucTotalByteCount += usLength;
  207. ASSERT(ucTotalByteCount == ulSuppCredSize);
  208. // Provide the supplimental credentials
  209. *NewCredentials = pcSuppCreds;
  210. *NewCredentialsSize = ulSuppCredSize;
  211. DebugLog((DEB_TRACE, "CredentialUpdateNotify: Succeeded in pre-calc of digest hashes Aux cred size 0x%x\n",
  212. ulSuppCredSize ));
  213. CleanUp:
  214. UnicodeStringFree(&ustrFlatDomainName);
  215. UnicodeStringFree(&ustrNetBios);
  216. DebugLog((DEB_TRACE_FUNC, "CredentialUpdateNotify: Leaving Status 0x%x\n", Status));
  217. return Status;
  218. }
  219. //
  220. // Free's the memory allocated by CredentialUpdateNotify
  221. //
  222. VOID
  223. CredentialUpdateFree(
  224. PVOID p
  225. )
  226. {
  227. if (p) {
  228. DigestFreeMemory(p);
  229. }
  230. }
  231. //+-------------------------------------------------------------------------
  232. //
  233. // Function: CredentialUpdateRegister
  234. //
  235. // Synopsis: This routine is called from LSA in order to obtain
  236. // the name of the supplemental credentials passed into this package
  237. // when a password is changed or set.
  238. //
  239. // Effects: Register with LSA that we want to be notified on PWD change
  240. //
  241. // Arguments:
  242. //
  243. // OUT CredentialName -- the name of credential tag in the supplemental
  244. // credentials. Note this memory is never freed
  245. // by SAM, but must remain valid for the lifetime
  246. // of the process.
  247. //
  248. // Requires: no global requirements
  249. //
  250. // Returns: TRUE
  251. //
  252. // Notes: wdigest.DLL needs to be registered (in the registry) as a
  253. // package that SAM calls out to in order for this routine
  254. // to be involked.
  255. // This will be run only on the DC
  256. //
  257. //
  258. //--------------------------------------------------------------------------
  259. BOOLEAN
  260. CredentialUpdateRegister(
  261. OUT UNICODE_STRING *CredentialName
  262. )
  263. {
  264. ASSERT(CredentialName);
  265. RtlInitUnicodeString(CredentialName, WDIGEST_SP_NAME);
  266. return TRUE;
  267. }
  268. //+--------------------------------------------------------------------
  269. //
  270. // Function: PrecalcForms
  271. //
  272. // Synopsis: given a username, realm, password form
  273. // H(username, realm, password)
  274. // H(UPPER(username), UPPER(realm), password)
  275. // H(LOWER(username), LOWER(realm))
  276. //
  277. // Effects: None
  278. //
  279. // Arguments: pustrUsername - pointer to unicode_string struct with account name
  280. // pustrRealm - pointer to unicode_string struct with realm
  281. // pustrPassword - pointer to unicode_string struct with cleartext password
  282. // pHash - pointer to byte buffer for ouput passwrd hash
  283. // piHashSize - pointer to size of the binary buffer passed in & bytes written on output
  284. //
  285. // Returns: STATUS_SUCCESS for normal completion
  286. // STATUS_BUFFER_TOO_SMALL - Hash buffer too small, iHashSize contains min size
  287. //
  288. // Notes: Form three versions:
  289. // format provided, Uppercase and Lowercase versions
  290. //
  291. //---------------------------------------------------------------------
  292. NTSTATUS PrecalcForms(
  293. IN PUNICODE_STRING pustrUsername,
  294. IN PUNICODE_STRING pustrRealm,
  295. IN PUNICODE_STRING pustrPassword,
  296. OUT PCHAR pHash,
  297. IN OUT PUSHORT piHashSize
  298. )
  299. {
  300. NTSTATUS Status = STATUS_SUCCESS;
  301. PCHAR pWhere = NULL;
  302. USHORT usLength = 0;
  303. UNICODE_STRING ustrTempUsername;
  304. UNICODE_STRING ustrTempRealm;
  305. DebugLog((DEB_TRACE_FUNC, "PrecalcForms: Entering\n"));
  306. ZeroMemory(&ustrTempUsername, sizeof(ustrTempUsername));
  307. ZeroMemory(&ustrTempRealm, sizeof(ustrTempRealm));
  308. if (*piHashSize < (MD5_HASH_BYTESIZE * 3))
  309. {
  310. Status = SEC_E_BUFFER_TOO_SMALL;
  311. DebugLog((DEB_ERROR, "PrecalcForms: Buffer size too small for multiple hashes 0x%x\n", Status));
  312. goto CleanUp;
  313. }
  314. pWhere = pHash;
  315. // First hash - use the input credentials
  316. usLength = MD5_HASH_BYTESIZE;
  317. Status = PrecalcDigestHash(pustrUsername,
  318. pustrRealm,
  319. pustrPassword,
  320. pWhere,
  321. &usLength);
  322. if ((!NT_SUCCESS (Status)) && (Status != STATUS_UNMAPPABLE_CHARACTER))
  323. {
  324. DebugLog((DEB_ERROR, "PrecalcForms: PrecalcDigestHash error 0x%x\n", Status));
  325. goto CleanUp;
  326. }
  327. pWhere += MD5_HASH_BYTESIZE;
  328. // Create local copies for case modificaiton
  329. Status = UnicodeStringDuplicate(&ustrTempUsername, pustrUsername);
  330. if (!NT_SUCCESS (Status))
  331. {
  332. DebugLog((DEB_ERROR, "PrecalcForms: Username copy error 0x%x\n", Status));
  333. goto CleanUp;
  334. }
  335. Status = UnicodeStringDuplicate(&ustrTempRealm, pustrRealm);
  336. if (!NT_SUCCESS (Status))
  337. {
  338. DebugLog((DEB_ERROR, "PrecalcForms: Realm copy error 0x%x\n", Status));
  339. goto CleanUp;
  340. }
  341. // Now form lowercase version
  342. Status = RtlDowncaseUnicodeString(&ustrTempUsername, &ustrTempUsername, FALSE);
  343. if (!NT_SUCCESS (Status))
  344. {
  345. DebugLog((DEB_ERROR, "PrecalcForms: Downcase failed error 0x%x\n", Status));
  346. goto CleanUp;
  347. }
  348. Status = RtlDowncaseUnicodeString(&ustrTempRealm, &ustrTempRealm, FALSE);
  349. if (!NT_SUCCESS (Status))
  350. {
  351. DebugLog((DEB_ERROR, "PrecalcForms: Downcase failed error 0x%x\n", Status));
  352. goto CleanUp;
  353. }
  354. usLength = MD5_HASH_BYTESIZE;
  355. Status = PrecalcDigestHash(&ustrTempUsername,
  356. &ustrTempRealm,
  357. pustrPassword,
  358. pWhere,
  359. &usLength);
  360. if ((!NT_SUCCESS (Status)) && (Status != STATUS_UNMAPPABLE_CHARACTER))
  361. {
  362. DebugLog((DEB_ERROR, "PrecalcForms: PrecalcDigestHash error 0x%x\n", Status));
  363. goto CleanUp;
  364. }
  365. pWhere += MD5_HASH_BYTESIZE;
  366. // Now form uppercase version
  367. Status = RtlUpcaseUnicodeString(&ustrTempUsername, &ustrTempUsername, FALSE);
  368. if (!NT_SUCCESS (Status))
  369. {
  370. DebugLog((DEB_ERROR, "PrecalcForms: Upcase failed error 0x%x\n", Status));
  371. goto CleanUp;
  372. }
  373. Status = RtlUpcaseUnicodeString(&ustrTempRealm, &ustrTempRealm, FALSE);
  374. if (!NT_SUCCESS (Status))
  375. {
  376. DebugLog((DEB_ERROR, "PrecalcForms: Upcase failed error 0x%x\n", Status));
  377. goto CleanUp;
  378. }
  379. usLength = MD5_HASH_BYTESIZE;
  380. Status = PrecalcDigestHash(&ustrTempUsername,
  381. &ustrTempRealm,
  382. pustrPassword,
  383. pWhere,
  384. &usLength);
  385. if ((!NT_SUCCESS (Status)) && (Status != STATUS_UNMAPPABLE_CHARACTER))
  386. {
  387. DebugLog((DEB_ERROR, "PrecalcForms: PrecalcDigestHash error 0x%x\n", Status));
  388. goto CleanUp;
  389. }
  390. // Indicate that we used three MD5 hashes
  391. *piHashSize = (MD5_HASH_BYTESIZE * 3);
  392. Status = STATUS_SUCCESS;
  393. CleanUp:
  394. UnicodeStringFree(&ustrTempUsername);
  395. UnicodeStringFree(&ustrTempRealm);
  396. DebugLog((DEB_TRACE_FUNC, "PrecalcForms: Leaving 0x%x\n", Status));
  397. return (Status);
  398. }
  399. //+--------------------------------------------------------------------
  400. //
  401. // Function: PrecalcDigestHash
  402. //
  403. // Synopsis: Calculate PasswordHash H(accountname:realm:password)
  404. //
  405. // Effects: None
  406. //
  407. // Arguments: pustrUsername - pointer to unicode_string struct with account name
  408. // pustrRealm - pointer to unicode_string struct with realm
  409. // pustrPassword - pointer to unicode_string struct with cleartext password
  410. // pHash - pointer to byte buffer for ouput passwrd hash
  411. // piHashSize - pointer to size of the binary buffer passed in & bytes written on output
  412. //
  413. // Returns: STATUS_SUCCESS for normal completion
  414. // STATUS_BUFFER_TOO_SMALL - Binary Hash buffer too small, iHashSize contains min size
  415. //
  416. // Notes: For each parameter, if it can be encoded fully in ISO 8859-1 then do so.
  417. // If not (there are extended characters), then encode in UTF-8. Each component is tested separately.
  418. //
  419. //---------------------------------------------------------------------
  420. NTSTATUS PrecalcDigestHash(
  421. IN PUNICODE_STRING pustrUsername,
  422. IN PUNICODE_STRING pustrRealm,
  423. IN PUNICODE_STRING pustrPassword,
  424. OUT PCHAR pHash,
  425. IN OUT PUSHORT piHashSize
  426. )
  427. {
  428. NTSTATUS Status = STATUS_SUCCESS;
  429. STRING strUsername;
  430. STRING strRealm;
  431. STRING strPasswd;
  432. STRING strHash;
  433. BOOL fDefChars = FALSE;
  434. DebugLog((DEB_TRACE_FUNC, "PrecalcDigestHash: Entering\n"));
  435. ZeroMemory(&strUsername, sizeof(strUsername));
  436. ZeroMemory(&strRealm, sizeof(strRealm));
  437. ZeroMemory(&strPasswd, sizeof(strPasswd));
  438. ZeroMemory(&strHash, sizeof(strHash));
  439. if (*piHashSize < MD5_HASH_BYTESIZE)
  440. {
  441. Status = STATUS_BUFFER_TOO_SMALL;
  442. DebugLog((DEB_TRACE_FUNC, "PrecalcDigestHash: Hash output buffer too small\n"));
  443. *piHashSize = MD5_HASH_BYTESIZE; // return how many bytes are needed to write out value
  444. }
  445. // First check if OK to encode in ISO 8859-1, if not then use UTF-8
  446. // All characters must be within ISO 8859-1 Character set else fail
  447. if (pustrUsername && pustrUsername->Length && pustrUsername->Buffer)
  448. {
  449. fDefChars = FALSE;
  450. Status = EncodeUnicodeString(pustrUsername, CP_8859_1, &strUsername, &fDefChars);
  451. if (!NT_SUCCESS(Status))
  452. {
  453. DebugLog((DEB_ERROR, "PrecalcDigestHash: Error in encoding username\n"));
  454. Status = SEC_E_INSUFFICIENT_MEMORY;
  455. goto CleanUp;
  456. }
  457. if (fDefChars == TRUE)
  458. {
  459. DebugLog((DEB_TRACE, "PrecalcDigestHash: Can not encode Username in 8859-1, use UTF-8\n"));
  460. StringFree(&strUsername);
  461. Status = EncodeUnicodeString(pustrUsername, CP_UTF8, &strUsername, NULL);
  462. if (!NT_SUCCESS(Status))
  463. {
  464. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding username\n"));
  465. Status = SEC_E_INSUFFICIENT_MEMORY;
  466. goto CleanUp;
  467. }
  468. }
  469. }
  470. if (pustrRealm && pustrRealm->Length && pustrRealm->Buffer)
  471. {
  472. fDefChars = FALSE;
  473. Status = EncodeUnicodeString(pustrRealm, CP_8859_1, &strRealm, &fDefChars);
  474. if (!NT_SUCCESS(Status))
  475. {
  476. DebugLog((DEB_ERROR, "PrecalcDigestHash: Error in encoding realm\n"));
  477. Status = SEC_E_INSUFFICIENT_MEMORY;
  478. goto CleanUp;
  479. }
  480. if (fDefChars == TRUE)
  481. {
  482. DebugLog((DEB_TRACE, "PrecalcDigestHash: Can not encode realm in 8859-1, use UTF-8\n"));
  483. StringFree(&strRealm);
  484. Status = EncodeUnicodeString(pustrRealm, CP_UTF8, &strRealm, NULL);
  485. if (!NT_SUCCESS(Status))
  486. {
  487. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding realm\n"));
  488. Status = SEC_E_INSUFFICIENT_MEMORY;
  489. goto CleanUp;
  490. }
  491. }
  492. }
  493. if (pustrPassword && pustrPassword->Length && pustrPassword->Buffer)
  494. {
  495. fDefChars = FALSE;
  496. Status = EncodeUnicodeString(pustrPassword, CP_8859_1, &strPasswd, &fDefChars);
  497. if (!NT_SUCCESS(Status))
  498. {
  499. DebugLog((DEB_ERROR, "PrecalcDigestHash: Error in encoding passwd\n"));
  500. Status = SEC_E_INSUFFICIENT_MEMORY;
  501. goto CleanUp;
  502. }
  503. if (fDefChars == TRUE)
  504. {
  505. DebugLog((DEB_TRACE, "PrecalcDigestHash: Can not encode password in 8859-1, use UTF-8\n"));
  506. if (strPasswd.Buffer && strPasswd.MaximumLength)
  507. {
  508. ZeroMemory(strPasswd.Buffer, strPasswd.MaximumLength);
  509. }
  510. StringFree(&strPasswd);
  511. Status = EncodeUnicodeString(pustrPassword, CP_UTF8, &strPasswd, NULL);
  512. if (!NT_SUCCESS(Status))
  513. {
  514. DebugLog((DEB_ERROR, "PrecalcDigestHash: Error in encoding passwd\n"));
  515. Status = SEC_E_INSUFFICIENT_MEMORY;
  516. goto CleanUp;
  517. }
  518. }
  519. }
  520. Status = StringAllocate(&strHash, MD5_HASH_BYTESIZE);
  521. if (!NT_SUCCESS(Status))
  522. {
  523. DebugLog((DEB_ERROR, "PrecalcDigestHash: No Memory\n"));
  524. Status = SEC_E_INSUFFICIENT_MEMORY;
  525. goto CleanUp;
  526. }
  527. // Use PasswdHash = H(username-value:realm-value:passwd)
  528. Status = DigestHash7(&strUsername,
  529. &strRealm,
  530. &strPasswd,
  531. NULL, NULL, NULL, NULL,
  532. FALSE, &strHash);
  533. if (!NT_SUCCESS(Status))
  534. {
  535. DebugLog((DEB_ERROR, "PrecalcDigestHash: H(U:R:PW) failed : 0x%x\n", Status));
  536. goto CleanUp;
  537. }
  538. #if DBG
  539. STRING strTempPwKey;
  540. ZeroMemory(&strTempPwKey, sizeof(strTempPwKey));
  541. MyPrintBytes(strHash.Buffer, strHash.Length, &strTempPwKey);
  542. DebugLog((DEB_TRACE, "DigestCalcHA1: Binary H(%Z:%Z:************) is %Z\n",
  543. &strUsername, &strRealm, &strTempPwKey));
  544. StringFree(&strTempPwKey);
  545. #endif
  546. memcpy(pHash, strHash.Buffer, MD5_HASH_BYTESIZE);
  547. *piHashSize = MD5_HASH_BYTESIZE;
  548. CleanUp:
  549. if (Status == STATUS_UNMAPPABLE_CHARACTER)
  550. {
  551. // We were unable to make the mapping to the selected codepage
  552. // Case exists if inputs are unicode characters not contained in ISO 8859-1 and codepage is ISO 8859-1
  553. // Zero out the hash
  554. ZeroMemory(pHash, sizeof(MD5_HASH_BYTESIZE));
  555. *piHashSize = MD5_HASH_BYTESIZE; // return how many bytes are needed to write out value
  556. }
  557. if (strPasswd.MaximumLength && strPasswd.Buffer)
  558. {
  559. // Make sure to erase any password info
  560. ZeroMemory(strPasswd.Buffer, strPasswd.MaximumLength);
  561. }
  562. StringFree(&strUsername);
  563. StringFree(&strRealm);
  564. StringFree(&strPasswd);
  565. StringFree(&strHash);
  566. DebugLog((DEB_TRACE_FUNC, "PrecalcDigestHash: Leaving Status 0x%x\n", Status));
  567. return Status;
  568. }