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.

438 lines
9.4 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. owf.c
  5. Abstract:
  6. Implentation of the one-way-functions used to implement password hashing.
  7. RtlCalculateLmOwfPassword
  8. RtlCalculateNtOwfPassword
  9. Author:
  10. David Chalmers (Davidc) 10-21-91
  11. Revision History:
  12. --*/
  13. #ifndef KMODE
  14. #define _ADVAPI32_
  15. #endif
  16. #include <nt.h>
  17. #include <ntrtl.h>
  18. #ifndef KMODE
  19. #include <nturtl.h>
  20. #endif
  21. #include <crypt.h>
  22. #include <engine.h>
  23. #ifndef KMODE
  24. #include <windef.h>
  25. #include <winbase.h>
  26. #include <wincrypt.h>
  27. #endif
  28. #ifndef KMODE
  29. //
  30. // Globals used for allowing the replacement of the OWF functions
  31. //
  32. HCRYPTPROV KerbGlobalStrToKeyProvider = 0;
  33. BOOLEAN KerbGlobalAvailableStrToKeyProvider = TRUE;
  34. //+-------------------------------------------------------------------------
  35. //
  36. // Function: CheckForOutsideStringToKey
  37. //
  38. // Synopsis: Call CryptoAPI to query to see if a CSP is registered
  39. // of the type PROV_REPLACE_OWF.
  40. //
  41. // Effects:
  42. //
  43. // Arguments:
  44. //
  45. // Requires:
  46. //
  47. // Returns: STATUS_SUCCESS if it succeeds, otherwise STATUS_UNSUCCESSFUL
  48. //
  49. // Notes:
  50. //
  51. //
  52. //--------------------------------------------------------------------------
  53. BOOLEAN
  54. CheckForOutsideStringToKey()
  55. {
  56. HCRYPTPROV hProv = 0;
  57. BOOLEAN fRet = FALSE;
  58. if (!KerbGlobalAvailableStrToKeyProvider)
  59. {
  60. goto Cleanup;
  61. }
  62. //
  63. // see if there is a replacement provider
  64. if (0 != KerbGlobalStrToKeyProvider)
  65. {
  66. // if there is proceed to use it
  67. fRet = TRUE;
  68. goto Cleanup;
  69. }
  70. else
  71. {
  72. //
  73. // Try to acquire a context to a CSP which is used for OWF replacement
  74. //
  75. if (!CryptAcquireContext(&hProv,
  76. NULL,
  77. NULL,
  78. PROV_REPLACE_OWF,
  79. CRYPT_VERIFYCONTEXT))
  80. {
  81. KerbGlobalAvailableStrToKeyProvider = FALSE;
  82. goto Cleanup;
  83. }
  84. //
  85. // exchange the local and the global in a safe way
  86. //
  87. if (0 != InterlockedCompareExchangePointer(
  88. (PVOID*)&KerbGlobalStrToKeyProvider,
  89. (PVOID)hProv,
  90. 0))
  91. {
  92. CryptReleaseContext(hProv, 0);
  93. }
  94. fRet = TRUE;
  95. }
  96. Cleanup:
  97. return fRet;
  98. }
  99. //+-------------------------------------------------------------------------
  100. //
  101. // Function: UseOutsideStringToKey
  102. //
  103. // Synopsis: Calls the CSP to do an outside StringToKey function
  104. // using the hashing entry points of CryptoAPI.
  105. //
  106. // Effects:
  107. //
  108. // Arguments:
  109. //
  110. // Requires:
  111. //
  112. // Returns:
  113. //
  114. // Notes:
  115. //
  116. //
  117. //--------------------------------------------------------------------------
  118. NTSTATUS
  119. UseOutsideStringToKey(
  120. IN PCHAR pPassword,
  121. IN USHORT cbPassword,
  122. IN ULONG ulFlags,
  123. IN ULONG cbKey,
  124. OUT PUCHAR pbKey
  125. )
  126. {
  127. HCRYPTHASH hHash = 0;
  128. ULONG cb;
  129. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  130. if (!CheckForOutsideStringToKey())
  131. {
  132. // STATUS_UNSUCCESSFUL indicates not to fallback to default OWF calculation
  133. // so we don't want to use that here
  134. Status = NTE_BAD_PROVIDER;
  135. goto Cleanup;
  136. }
  137. //
  138. // create the hash
  139. //
  140. if (!CryptCreateHash(KerbGlobalStrToKeyProvider,
  141. CALG_HASH_REPLACE_OWF,
  142. 0,
  143. 0,
  144. &hHash))
  145. {
  146. goto Cleanup;
  147. }
  148. //
  149. // hash the password
  150. //
  151. if (!CryptHashData(hHash,
  152. pPassword,
  153. (ULONG)cbPassword,
  154. ulFlags))
  155. {
  156. if (NTE_BAD_DATA == GetLastError())
  157. {
  158. Status = NTE_BAD_DATA;
  159. }
  160. goto Cleanup;
  161. }
  162. //
  163. // Get the HP_HASHVAL, this is the key
  164. //
  165. cb = cbKey;
  166. if (!CryptGetHashParam(hHash,
  167. HP_HASHVAL,
  168. pbKey,
  169. &cb,
  170. 0))
  171. {
  172. if (NTE_BAD_LEN == GetLastError())
  173. {
  174. Status = NTE_BAD_DATA;
  175. }
  176. goto Cleanup;
  177. }
  178. Status = STATUS_SUCCESS;
  179. Cleanup:
  180. if (0 != hHash)
  181. {
  182. CryptDestroyHash(hHash);
  183. }
  184. return Status;
  185. }
  186. #endif
  187. NTSTATUS
  188. RtlCalculateLmOwfPassword(
  189. IN PLM_PASSWORD LmPassword,
  190. OUT PLM_OWF_PASSWORD LmOwfPassword
  191. )
  192. /*++
  193. Routine Description:
  194. Takes the passed LmPassword and performs a one-way-function on it.
  195. The current implementation does this by using the password as a key
  196. to encrypt a known block of text.
  197. Arguments:
  198. LmPassword - The password to perform the one-way-function on.
  199. LmOwfPassword - The hashed password is returned here
  200. Return Values:
  201. STATUS_SUCCESS - The function was completed successfully. The hashed
  202. password is in LmOwfPassword.
  203. STATUS_UNSUCCESSFUL - Something failed. The LmOwfPassword is undefined.
  204. --*/
  205. {
  206. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  207. BLOCK_KEY Key[2];
  208. PCHAR pKey;
  209. #ifndef KMODE
  210. Status = UseOutsideStringToKey(
  211. LmPassword,
  212. (USHORT)strlen(LmPassword),
  213. sizeof(LM_OWF_PASSWORD),
  214. CRYPT_OWF_REPL_LM_HASH,
  215. (PUCHAR)&(LmOwfPassword->data[0])
  216. );
  217. //
  218. // the function will return STATUS_UNSUCCESSFUL indicates not to fall
  219. // back to the typical string to key function.
  220. //
  221. if ((NT_SUCCESS(Status)) || (STATUS_UNSUCCESSFUL == Status))
  222. {
  223. return Status;
  224. }
  225. #endif
  226. // Copy the password into our key buffer and zero pad to fill the 2 keys
  227. pKey = (PCHAR)(&Key[0]);
  228. while (*LmPassword && (pKey < (PCHAR)(&Key[2]))) {
  229. *pKey++ = *LmPassword++;
  230. }
  231. while (pKey < (PCHAR)(&Key[2])) {
  232. *pKey++ = 0;
  233. }
  234. // Use the keys to encrypt the standard text
  235. Status = RtlEncryptStdBlock(&Key[0], &(LmOwfPassword->data[0]));
  236. if (!NT_SUCCESS(Status)) {
  237. return(Status);
  238. }
  239. Status = RtlEncryptStdBlock(&Key[1], &(LmOwfPassword->data[1]));
  240. //
  241. // clear our copy of the cleartext password
  242. //
  243. pKey = (PCHAR)(&Key[0]);
  244. while (pKey < (PCHAR)(&Key[2])) {
  245. *pKey++ = 0;
  246. }
  247. RtlSecureZeroMemory( &Key, sizeof(Key) );
  248. return(Status);
  249. }
  250. NTSTATUS
  251. RtlCalculateNtOwfPassword(
  252. IN PNT_PASSWORD NtPassword,
  253. OUT PNT_OWF_PASSWORD NtOwfPassword
  254. )
  255. /*++
  256. Routine Description:
  257. Takes the passed NtPassword and performs a one-way-function on it.
  258. Uses the RSA MD4 function
  259. Arguments:
  260. NtPassword - The password to perform the one-way-function on.
  261. NtOwfPassword - The hashed password is returned here
  262. Return Values:
  263. STATUS_SUCCESS - The function was completed successfully. The hashed
  264. password is in NtOwfPassword.
  265. --*/
  266. {
  267. MD4_CTX MD4_Context;
  268. NTSTATUS Status = STATUS_UNSUCCESSFUL;
  269. #ifndef KMODE
  270. Status = UseOutsideStringToKey(
  271. (PCHAR)NtPassword->Buffer,
  272. (USHORT)NtPassword->Length,
  273. 0,
  274. sizeof(*NtOwfPassword),
  275. (PUCHAR)NtOwfPassword
  276. );
  277. //
  278. // the function will return STATUS_UNSUCCESSFUL indicates not to fall
  279. // back to the typical string to key function.
  280. //
  281. if ((NT_SUCCESS(Status)) || (STATUS_UNSUCCESSFUL == Status))
  282. {
  283. return Status;
  284. }
  285. #endif
  286. MD4Init(&MD4_Context);
  287. MD4Update(&MD4_Context, (PCHAR)NtPassword->Buffer, NtPassword->Length);
  288. MD4Final(&MD4_Context);
  289. // Copy the digest into our return data area
  290. ASSERT(sizeof(*NtOwfPassword) == sizeof(MD4_Context.digest));
  291. RtlCopyMemory((PVOID)NtOwfPassword, (PVOID)MD4_Context.digest,
  292. sizeof(*NtOwfPassword));
  293. RtlSecureZeroMemory( &MD4_Context, sizeof(MD4_Context) );
  294. return(STATUS_SUCCESS);
  295. }
  296. BOOLEAN
  297. RtlEqualLmOwfPassword(
  298. IN PLM_OWF_PASSWORD LmOwfPassword1,
  299. IN PLM_OWF_PASSWORD LmOwfPassword2
  300. )
  301. /*++
  302. Routine Description:
  303. Compares two Lanman One-way-function-passwords
  304. Arguments:
  305. LmOwfPassword1/2 - The one-way-functions to compare
  306. Return Values:
  307. TRUE if the one-way-functions match, otherwise FALSE
  308. --*/
  309. {
  310. return((BOOLEAN)(RtlCompareMemory(LmOwfPassword1,
  311. LmOwfPassword2,
  312. LM_OWF_PASSWORD_LENGTH)
  313. == LM_OWF_PASSWORD_LENGTH));
  314. }
  315. BOOLEAN
  316. RtlEqualNtOwfPassword(
  317. IN PNT_OWF_PASSWORD NtOwfPassword1,
  318. IN PNT_OWF_PASSWORD NtOwfPassword2
  319. )
  320. /*++
  321. Routine Description:
  322. Compares two NT One-way-function-passwords
  323. Arguments:
  324. NtOwfPassword1/2 - The one-way-functions to compare
  325. Return Values:
  326. TRUE if the one-way-functions match, otherwise FALSE
  327. --*/
  328. {
  329. return((BOOLEAN)(RtlCompareMemory(NtOwfPassword1,
  330. NtOwfPassword2,
  331. NT_OWF_PASSWORD_LENGTH)
  332. == NT_OWF_PASSWORD_LENGTH));
  333. }