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.

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