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.

1656 lines
54 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 2000
  6. //
  7. // File: auth.cxx
  8. //
  9. // Contents: Digest Access creation & validation
  10. // Main entry points into this dll:
  11. // DigestIsValid
  12. // NonceValidate
  13. // NonceInitialize
  14. //
  15. // History: KDamour 16Mar00 Created
  16. //
  17. //------------------------------------------------------------------------
  18. #include "global.h"
  19. #include <lmcons.h> // For Max Passwd Length PWLEN
  20. #include <stdio.h>
  21. //+--------------------------------------------------------------------
  22. //
  23. // Function: DigestCalculation
  24. //
  25. // Synopsis: Perform Digest Access Calculation
  26. //
  27. // Effects:
  28. //
  29. // Arguments: pDigest - pointer to digest access data fields
  30. //
  31. // Returns: STATUS_SUCCESS for normal completion
  32. //
  33. // Notes: This routine can be used for both calculating and verifying
  34. // the Digest Access nonce value. The Switching parameter is
  35. // pDigest->type. Calling routine must provide the space for any
  36. // returned hashed values (like pReqDigest).
  37. //
  38. // For clients, the cleartext password must be avilable to generate the
  39. // session key.
  40. //
  41. // After the initial ISC/ASC completes, a copy of the sessionkey is kept in
  42. // in the context and copied over to the Digest structure. The username, realm
  43. // and password are not utilized from the UserCreds since we already have a
  44. // sessionkey.
  45. //
  46. //---------------------------------------------------------------------
  47. NTSTATUS NTAPI
  48. DigestCalculation(
  49. IN PDIGEST_PARAMETER pDigest,
  50. IN PUSER_CREDENTIALS pUserCreds
  51. )
  52. {
  53. NTSTATUS Status = E_FAIL;
  54. DebugLog((DEB_TRACE_FUNC, "DigestCalculation: Entering\n"));
  55. Status = DigestIsValid(pDigest);
  56. if (!NT_SUCCESS(Status))
  57. {
  58. return(Status);
  59. }
  60. switch (pDigest->typeDigest)
  61. {
  62. case DIGEST_CLIENT: // Called by clients to generate Response
  63. case SASL_CLIENT: // Called by clients to generate Response
  64. {
  65. Status = DigestCalcChalRsp(pDigest, pUserCreds, FALSE);
  66. break;
  67. }
  68. case DIGEST_SERVER:
  69. case SASL_SERVER:
  70. {
  71. Status = DigestCalcChalRsp(pDigest, pUserCreds, TRUE);
  72. break;
  73. }
  74. default:
  75. { // No Digest calculations for that yet
  76. Status = SEC_E_UNSUPPORTED_FUNCTION;
  77. DebugLog((DEB_ERROR, "NTDigest: Unsupported typeDigest = %d\n", pDigest->typeDigest));
  78. break;
  79. }
  80. }
  81. DebugLog((DEB_TRACE_FUNC, "DigestCalculation: Leaving Status 0x%x\n", Status));
  82. return(Status);
  83. }
  84. //+--------------------------------------------------------------------
  85. //
  86. // Function: DigestIsValid
  87. //
  88. // Synopsis: Simple checks for enough data for Digest calculation
  89. //
  90. // Effects:
  91. //
  92. // Arguments: pDigest - pointer to digest access data fields
  93. //
  94. // Returns: STATUS_SUCCESS for normal completion
  95. //
  96. // Notes:
  97. //
  98. //---------------------------------------------------------------------
  99. NTSTATUS NTAPI
  100. DigestIsValid(
  101. IN PDIGEST_PARAMETER pDigest
  102. )
  103. {
  104. NTSTATUS Status = STATUS_SUCCESS;
  105. int i = 0;
  106. DebugLog((DEB_TRACE_FUNC, "DigestIsValid: Entering\n"));
  107. if (!pDigest)
  108. { // Fail on no Digest Parameters passed in
  109. DebugLog((DEB_ERROR, "DigestIsValid: no digest pointer arg\n"));
  110. Status = STATUS_INVALID_PARAMETER;
  111. goto CleanUp;
  112. }
  113. // Check for proper struct format for strings
  114. for (i=0; i< MD5_AUTH_LAST;i++)
  115. {
  116. if (!NT_SUCCESS(StringVerify(&(pDigest->refstrParam[i]))))
  117. {
  118. DebugLog((DEB_ERROR, "DigestIsValid: Digest String struct bad format\n"));
  119. Status = STATUS_INVALID_PARAMETER;
  120. goto CleanUp;
  121. }
  122. }
  123. if (!NT_SUCCESS(StringVerify(&(pDigest->strSessionKey))))
  124. {
  125. DebugLog((DEB_ERROR, "DigestIsValid: Digest String struct bad format in SessionKey\n"));
  126. Status = STATUS_INVALID_PARAMETER;
  127. goto CleanUp;
  128. }
  129. // Do some required checks for field-value data
  130. // Required Username-value, Realm-value, nonce, Method
  131. // URI
  132. if ((!pDigest->refstrParam[MD5_AUTH_NONCE].Length) ||
  133. (!pDigest->refstrParam[MD5_AUTH_METHOD].Length) ||
  134. (!pDigest->refstrParam[MD5_AUTH_URI].Length)
  135. )
  136. {
  137. // Failed on a require field-value
  138. DebugLog((DEB_ERROR, "DigestIsValid: required digest field missing\n"));
  139. Status = STATUS_INVALID_PARAMETER;
  140. goto CleanUp;
  141. }
  142. CleanUp:
  143. DebugLog((DEB_TRACE_FUNC, "DigestIsValid: Leaving Status 0x%x\n", Status));
  144. return(Status);
  145. }
  146. //+--------------------------------------------------------------------
  147. //
  148. // Function: DigestFree
  149. //
  150. // Synopsis: Clear out the digest & free memory from Digest struct
  151. //
  152. // Effects:
  153. //
  154. // Arguments: pDigest - pointer to digest access data fields
  155. //
  156. // Returns: STATUS_SUCCESS for normal completion
  157. //
  158. // Notes: This should be called when done with a DIGEST_PARAMTER object
  159. //
  160. //
  161. //---------------------------------------------------------------------
  162. NTSTATUS NTAPI
  163. DigestFree(
  164. IN PDIGEST_PARAMETER pDigest
  165. )
  166. {
  167. NTSTATUS Status = STATUS_SUCCESS;
  168. int i = 0;
  169. DebugLog((DEB_TRACE_FUNC, "Entering DigestFree\n"));
  170. if (!pDigest)
  171. {
  172. return STATUS_INVALID_PARAMETER;
  173. }
  174. StringFree(&pDigest->strSessionKey);
  175. StringFree(&pDigest->strResponse);
  176. StringFree(&pDigest->strUsernameEncoded);
  177. StringFree(&pDigest->strRealmEncoded);
  178. UnicodeStringFree(&pDigest->ustrRealm);
  179. UnicodeStringFree(&pDigest->ustrUsername);
  180. UnicodeStringFree(&pDigest->ustrUri);
  181. UnicodeStringFree(&pDigest->ustrCrackedAccountName);
  182. UnicodeStringFree(&pDigest->ustrCrackedDomain);
  183. // Release any directive storage
  184. // This was used to remove backslash encoding from directives
  185. for (i = 0; i < MD5_AUTH_LAST; i++)
  186. {
  187. StringFree(&(pDigest->strDirective[i]));
  188. }
  189. DebugLog((DEB_TRACE_FUNC, "Leaving DigestFree\n"));
  190. return(Status);
  191. }
  192. //+--------------------------------------------------------------------
  193. //
  194. // Function: DigestDecodeDirectiveStrings
  195. //
  196. // Synopsis: Processed parsed digest auth message and fill in string values
  197. //
  198. // Effects:
  199. //
  200. // Arguments: pDigest - pointer to digest access data fields
  201. //
  202. // Returns: STATUS_SUCCESS for normal completion
  203. //
  204. // Notes:
  205. //
  206. //---------------------------------------------------------------------
  207. NTSTATUS NTAPI
  208. DigestDecodeDirectiveStrings(
  209. IN PDIGEST_PARAMETER pDigest
  210. )
  211. {
  212. NTSTATUS Status = STATUS_SUCCESS;
  213. DebugLog((DEB_TRACE_FUNC, "DigestDecodeDirectiveStrings Entering\n"));
  214. if (!pDigest)
  215. {
  216. return STATUS_INVALID_PARAMETER;
  217. }
  218. // Decode URI, Username, Realm
  219. // Decode the username and realm directives
  220. if (pDigest->typeCharset == UTF_8)
  221. {
  222. DebugLog((DEB_TRACE, "DigestDecodeDirectiveStrings: UTF-8 Character set decoding\n"));
  223. Status = DecodeUnicodeString(&(pDigest->refstrParam[MD5_AUTH_USERNAME]),
  224. CP_UTF8,
  225. &pDigest->ustrUsername);
  226. if (!NT_SUCCESS (Status))
  227. {
  228. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: DecodeUnicodeString error 0x%x\n", Status));
  229. goto CleanUp;
  230. }
  231. Status = DecodeUnicodeString(&(pDigest->refstrParam[MD5_AUTH_REALM]),
  232. CP_UTF8,
  233. &pDigest->ustrRealm);
  234. if (!NT_SUCCESS (Status))
  235. {
  236. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: DecodeUnicodeString error 0x%x\n", Status));
  237. goto CleanUp;
  238. }
  239. Status = DecodeUnicodeString(&(pDigest->refstrParam[MD5_AUTH_URI]),
  240. CP_UTF8,
  241. &pDigest->ustrUri);
  242. if (!NT_SUCCESS (Status))
  243. {
  244. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: DecodeUnicodeString error 0x%x\n", Status));
  245. goto CleanUp;
  246. }
  247. }
  248. else if (pDigest->typeCharset == ISO_8859_1)
  249. {
  250. DebugLog((DEB_TRACE, "DigestDecodeDirectiveStrings: ISO-8859-1 Character set decoding\n"));
  251. Status = DecodeUnicodeString(&(pDigest->refstrParam[MD5_AUTH_USERNAME]),
  252. CP_8859_1,
  253. &pDigest->ustrUsername);
  254. if (!NT_SUCCESS (Status))
  255. {
  256. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: DecodeUnicodeString Username error 0x%x\n", Status));
  257. goto CleanUp;
  258. }
  259. Status = DecodeUnicodeString(&(pDigest->refstrParam[MD5_AUTH_REALM]),
  260. CP_8859_1,
  261. &pDigest->ustrRealm);
  262. if (!NT_SUCCESS (Status))
  263. {
  264. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: DecodeUnicodeString Realm error 0x%x\n", Status));
  265. goto CleanUp;
  266. }
  267. Status = DecodeUnicodeString(&(pDigest->refstrParam[MD5_AUTH_URI]),
  268. CP_8859_1,
  269. &pDigest->ustrUri);
  270. if (!NT_SUCCESS (Status))
  271. {
  272. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: DecodeUnicodeString error 0x%x\n", Status));
  273. goto CleanUp;
  274. }
  275. }
  276. else
  277. {
  278. Status = STATUS_UNMAPPABLE_CHARACTER;
  279. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: Unknown CharacterSet encoding for Digest parameters\n"));
  280. goto CleanUp;
  281. }
  282. DebugLog((DEB_TRACE, "DigestDecodeDirectiveStrings: Processing username (%wZ) realm (%wZ) URI (%wZ)\n",
  283. &pDigest->ustrUsername,
  284. &pDigest->ustrRealm,
  285. &pDigest->ustrUri));
  286. CleanUp:
  287. DebugLog((DEB_TRACE_FUNC, "DigestDecodeStrings Leaving\n"));
  288. return(Status);
  289. }
  290. //+--------------------------------------------------------------------
  291. //
  292. // Function: DigestInit
  293. //
  294. // Synopsis: Initialize a DIGEST_PARAMETER structure
  295. //
  296. // Effects:
  297. //
  298. // Arguments: pDigest - pointer to digest access data fields
  299. //
  300. // Returns: STATUS_SUCCESS for normal completion
  301. //
  302. // Notes: This should be called when creating a DIGEST_PARAMTER object
  303. //
  304. //
  305. //---------------------------------------------------------------------
  306. NTSTATUS NTAPI
  307. DigestInit(
  308. IN PDIGEST_PARAMETER pDigest
  309. )
  310. {
  311. NTSTATUS Status = STATUS_SUCCESS;
  312. if (!pDigest)
  313. {
  314. return STATUS_INVALID_PARAMETER;
  315. }
  316. RtlZeroMemory(pDigest, sizeof(DIGEST_PARAMETER));
  317. // Now allocate the fixed length output buffers
  318. Status = StringAllocate(&(pDigest->strResponse), MD5_HASH_HEX_SIZE);
  319. if (!NT_SUCCESS(Status))
  320. {
  321. DebugLog((DEB_ERROR, "NTDigest:DigestCalculation No Memory\n"));
  322. Status = SEC_E_INSUFFICIENT_MEMORY;
  323. return(Status);
  324. }
  325. return(Status);
  326. }
  327. //+--------------------------------------------------------------------
  328. //
  329. // Function: DigestCalcChalRsp
  330. //
  331. // Synopsis: Perform Digest Access Calculation for ChallengeResponse
  332. //
  333. // Effects:
  334. //
  335. // Arguments: pDigest - pointer to digest access data fields
  336. // bIsChallenge - if TRUE then check Response provided (for HTTP Response)
  337. // - if FALSE then calculate Response (for HTTP Request)
  338. //
  339. // Returns: STATUS_SUCCESS for normal completion
  340. //
  341. // Notes: Called from DigestCalculation
  342. //
  343. //
  344. //---------------------------------------------------------------------
  345. NTSTATUS NTAPI
  346. DigestCalcChalRsp(IN PDIGEST_PARAMETER pDigest,
  347. IN PUSER_CREDENTIALS pUserCreds,
  348. BOOL IsChallenge)
  349. {
  350. NTSTATUS Status = E_FAIL;
  351. STRING strHA2;
  352. STRING strReqDigest; // Final request digest access value
  353. STRING strcQOP; // String pointing to a constant CZ - no need to free up
  354. int i = 0;
  355. DebugLog((DEB_TRACE_FUNC, "DigestCalcChalRsp: Entering\n"));
  356. ZeroMemory(&strHA2, sizeof(strHA2));
  357. ZeroMemory(&strReqDigest, sizeof(strReqDigest));
  358. ZeroMemory(&strcQOP, sizeof(strcQOP));
  359. // Make sure that there is a Request-Digest to Compare to (IsChallenge TRUE) or
  360. // Set (IsChallenge FALSE)
  361. if (IsChallenge && (!(pDigest->refstrParam[MD5_AUTH_RESPONSE].Length)))
  362. {
  363. // Failed on a require field-value
  364. DebugLog((DEB_ERROR, "DigestCalcChalRsp: No ChallengeResponse\n"));
  365. Status = STATUS_INVALID_PARAMETER;
  366. return(Status);
  367. }
  368. // Initialize local variables
  369. Status = StringAllocate(&strHA2, MD5_HASH_HEX_SIZE);
  370. if (!NT_SUCCESS(Status))
  371. {
  372. DebugLog((DEB_ERROR, "DigestCalcChalRsp: No Memory\n"));
  373. Status = SEC_E_INSUFFICIENT_MEMORY;
  374. goto CleanUp;
  375. }
  376. Status = StringAllocate(&strReqDigest, MD5_HASH_HEX_SIZE);
  377. if (!NT_SUCCESS(Status))
  378. {
  379. DebugLog((DEB_ERROR, "DigestCalcChalRsp: No Memory\n"));
  380. Status = SEC_E_INSUFFICIENT_MEMORY;
  381. goto CleanUp;
  382. }
  383. // Establish which QOP utilized
  384. if (pDigest->typeQOP == AUTH_CONF)
  385. {
  386. RtlInitString(&strcQOP, AUTHCONFSTR);
  387. }
  388. else if (pDigest->typeQOP == AUTH_INT)
  389. {
  390. RtlInitString(&strcQOP, AUTHINTSTR);
  391. }
  392. else if (pDigest->typeQOP == AUTH)
  393. {
  394. RtlInitString(&strcQOP, AUTHSTR);
  395. }
  396. // Check if already calculated H(A1) the session key
  397. // Well for Algorithm=MD5 it is just H(username:realm:passwd)
  398. if (!(pDigest->strSessionKey.Length))
  399. {
  400. // No Session Key calculated yet - create one & store it
  401. DebugLog((DEB_TRACE, "DigestCalcChalRsp: No session key calculated, generate one\n"));
  402. Status = DigestCalcHA1(pDigest, pUserCreds);
  403. if (!NT_SUCCESS(Status))
  404. {
  405. goto CleanUp;
  406. }
  407. }
  408. // We now have calculated H(A1)
  409. // Calculate H(A2)
  410. // For QOP unspecified or "auth" H(A2) = H( Method: URI)
  411. // For QOP Auth-int or Auth-conf H(A2) = H( Method: URI: H(entity-body))
  412. if ((pDigest->typeQOP == AUTH) || (pDigest->typeQOP == NO_QOP_SPECIFIED))
  413. {
  414. // Unspecified or Auth
  415. DebugLog((DEB_TRACE, "DigestCalcChalRsp: H(A2) using AUTH/Unspecified\n"));
  416. Status = DigestHash7(&(pDigest->refstrParam[MD5_AUTH_METHOD]),
  417. &(pDigest->refstrParam[MD5_AUTH_URI]),
  418. NULL, NULL, NULL, NULL, NULL,
  419. TRUE, &strHA2);
  420. if (!NT_SUCCESS(Status))
  421. {
  422. DebugLog((DEB_ERROR, "DigestCalcChalRsp H(A2) failed : 0x%x\n", Status));
  423. goto CleanUp;
  424. }
  425. }
  426. else
  427. {
  428. // Auth-int or Auth-conf
  429. DebugLog((DEB_TRACE, "DigestCalcChalRsp: H(A2) using AUTH-INT/CONF\n"));
  430. if (pDigest->refstrParam[MD5_AUTH_HENTITY].Length == 0)
  431. {
  432. Status = STATUS_INVALID_PARAMETER;
  433. DebugLog((DEB_ERROR, "DigestCalChalRsp HEntity Missing\n"));
  434. goto CleanUp;
  435. }
  436. Status = DigestHash7(&(pDigest->refstrParam[MD5_AUTH_METHOD]),
  437. &(pDigest->refstrParam[MD5_AUTH_URI]),
  438. &(pDigest->refstrParam[MD5_AUTH_HENTITY]),
  439. NULL, NULL, NULL, NULL,
  440. TRUE, &strHA2);
  441. if (!NT_SUCCESS(Status))
  442. {
  443. DebugLog((DEB_ERROR, "DigestCalcChalRsp H(A2) auth-int failed : 0x%x\n", Status));
  444. goto CleanUp;
  445. }
  446. }
  447. // We now have calculated H(A2)
  448. // Calculate Request-Digest
  449. // For QOP of Auth, Auth-int, Auth-conf Req-Digest = H( H(A1): nonce: nc: cnonce: qop: H(A2))
  450. // For QOP unspecified (old format) Req-Digest = H( H(A1): nonce: H(A2))
  451. if (pDigest->typeQOP != NO_QOP_SPECIFIED)
  452. {
  453. // Auth, Auth-int, Auth-conf
  454. if (pDigest->refstrParam[MD5_AUTH_NC].Length == 0)
  455. {
  456. Status = STATUS_INVALID_PARAMETER;
  457. DebugLog((DEB_ERROR, "DigestCalcChalRsp NC Missing\n"));
  458. goto CleanUp;
  459. }
  460. Status = DigestHash7(&(pDigest->strSessionKey),
  461. &(pDigest->refstrParam[MD5_AUTH_NONCE]),
  462. &(pDigest->refstrParam[MD5_AUTH_NC]),
  463. &(pDigest->refstrParam[MD5_AUTH_CNONCE]),
  464. &strcQOP,
  465. &strHA2, NULL,
  466. TRUE, &strReqDigest);
  467. if (!NT_SUCCESS(Status))
  468. {
  469. DebugLog((DEB_ERROR, "DigestCalcChalRsp Req-Digest failed : 0x%x\n", Status));
  470. goto CleanUp;
  471. }
  472. }
  473. else
  474. {
  475. // Unspecified backwards compat for RFC 2069
  476. Status = DigestHash7(&(pDigest->strSessionKey),
  477. &(pDigest->refstrParam[MD5_AUTH_NONCE]),
  478. &strHA2,
  479. NULL, NULL, NULL, NULL,
  480. TRUE, &strReqDigest);
  481. if (!NT_SUCCESS(Status))
  482. {
  483. DebugLog((DEB_ERROR, "DigestCalcChalRsp Req-Digest old format failed : 0x%x\n", Status));
  484. goto CleanUp;
  485. }
  486. }
  487. if (IsChallenge)
  488. {
  489. // We now have the Request-Digest so just compare to see if they match!
  490. if (!strncmp(pDigest->refstrParam[MD5_AUTH_RESPONSE].Buffer, strReqDigest.Buffer, 2*MD5_HASH_BYTESIZE))
  491. {
  492. DebugLog((DEB_TRACE, "DigestCalcChalRsp Request-Digest Matches!\n"));
  493. Status = STATUS_SUCCESS;
  494. }
  495. else
  496. {
  497. DebugLog((DEB_ERROR, "DigestCalcChalRsp Request-Digest FAILED.\n"));
  498. Status = STATUS_WRONG_PASSWORD;
  499. }
  500. }
  501. else
  502. {
  503. // We are to calculate the response-value so just set it (Hash Size + NULL)
  504. if (pDigest->strResponse.MaximumLength >= (MD5_HASH_HEX_SIZE + 1))
  505. {
  506. memcpy(pDigest->strResponse.Buffer, strReqDigest.Buffer, (MD5_HASH_HEX_SIZE + 1));
  507. pDigest->strResponse.Length = MD5_HASH_HEX_SIZE; // No Count NULL
  508. Status = STATUS_SUCCESS;
  509. }
  510. else
  511. {
  512. DebugLog((DEB_ERROR, "DigestCalcChalRsp Request-Digest Size too small.\n"));
  513. Status = STATUS_BUFFER_TOO_SMALL;
  514. }
  515. }
  516. CleanUp:
  517. StringFree(&strHA2);
  518. StringFree(&strReqDigest);
  519. DebugLog((DEB_TRACE_FUNC, "DigestCalcChalRsp: Leaving Status 0x%x\n", Status));
  520. return(Status);
  521. }
  522. //+--------------------------------------------------------------------
  523. //
  524. // Function: DigestCalcHA1
  525. //
  526. // Synopsis: Determine H(A1) for Digest Access
  527. //
  528. // Effects: Will calculate the SessionKey and store it in pDigest
  529. //
  530. // Arguments: pDigest - pointer to digest access data fields
  531. //
  532. // Returns: STATUS_SUCCESS for normal completion
  533. //
  534. // Notes: Called from DigestCalChalRsp
  535. // Sessionkey is H(A1)
  536. // Username and realm will be taken from the UserCreds
  537. //
  538. //---------------------------------------------------------------------
  539. NTSTATUS NTAPI
  540. DigestCalcHA1(IN PDIGEST_PARAMETER pDigest,
  541. IN PUSER_CREDENTIALS pUserCreds)
  542. {
  543. NTSTATUS Status = E_FAIL;
  544. UNICODE_STRING ustrTempPasswd = {0};
  545. STRING strHPwKey = {0};
  546. STRING strBinaryHPwKey = {0};
  547. STRING strHA0Base = {0};
  548. STRING strHA0 = {0};
  549. STRING strPasswd = {0};
  550. STRING strUsername = {0};
  551. STRING strRealm = {0};
  552. PSTRING pstrAuthzID = NULL;
  553. LONG rc = 0;
  554. ULONG ulVersion = 0;
  555. BOOL fDefChars = FALSE;
  556. USHORT usHashOffset = 0;
  557. BOOL fSASLMode = FALSE;
  558. BOOL fValidHash = FALSE;
  559. int i = 0;
  560. DebugLog((DEB_TRACE_FUNC, "DigestCalcHA1: Entering\n"));
  561. ASSERT(pDigest);
  562. ASSERT(pUserCreds);
  563. if (!pUserCreds)
  564. {
  565. // No username & domain passed in
  566. Status = STATUS_INVALID_PARAMETER;
  567. DebugLog((DEB_ERROR, "DigestCalcHA1: No username info passed in\n"));
  568. goto CleanUp;
  569. }
  570. if ((pDigest->typeDigest == SASL_SERVER) || (pDigest->typeDigest == SASL_CLIENT))
  571. {
  572. fSASLMode = TRUE;
  573. }
  574. // Initialize local variables
  575. Status = StringAllocate(&strBinaryHPwKey, MD5_HASH_BYTESIZE);
  576. if (!NT_SUCCESS(Status))
  577. {
  578. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  579. Status = SEC_E_INSUFFICIENT_MEMORY;
  580. goto CleanUp;
  581. }
  582. Status = StringAllocate(&strHA0Base, MD5_HASH_HEX_SIZE);
  583. if (!NT_SUCCESS(Status))
  584. {
  585. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  586. Status = SEC_E_INSUFFICIENT_MEMORY;
  587. goto CleanUp;
  588. }
  589. Status = StringAllocate(&strHA0, MD5_HASH_HEX_SIZE);
  590. if (!NT_SUCCESS(Status))
  591. {
  592. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  593. Status = SEC_E_INSUFFICIENT_MEMORY;
  594. goto CleanUp;
  595. }
  596. // Check outputs
  597. if (pDigest->strSessionKey.MaximumLength <= MD5_HASH_HEX_SIZE)
  598. {
  599. StringFree(&(pDigest->strSessionKey));
  600. Status = StringAllocate(&(pDigest->strSessionKey), MD5_HASH_HEX_SIZE);
  601. if (!NT_SUCCESS(Status))
  602. {
  603. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  604. Status = SEC_E_INSUFFICIENT_MEMORY;
  605. goto CleanUp;
  606. }
  607. }
  608. if ((pUserCreds->fIsValidDigestHash == TRUE) && (pUserCreds->wHashSelected > 0))
  609. {
  610. // selected pre-calculated hash - retrieve from userCreds
  611. // read in precalc version number
  612. if (pUserCreds->strDigestHash.Length < MD5_HASH_BYTESIZE)
  613. {
  614. DebugLog((DEB_ERROR, "DigestCalcHA1: No Header on Precalc hash\n"));
  615. Status = SEC_E_NO_CREDENTIALS;
  616. goto CleanUp;
  617. }
  618. // check if valid hash number
  619. usHashOffset = pUserCreds->wHashSelected * MD5_HASH_BYTESIZE;
  620. if (pUserCreds->strDigestHash.Length < (usHashOffset + MD5_HASH_BYTESIZE))
  621. {
  622. DebugLog((DEB_ERROR, "DigestCalcHA1: Invalid Pre-calc\n"));
  623. Status = SEC_E_NO_CREDENTIALS;
  624. goto CleanUp;
  625. }
  626. // extract pre-calc hash - this is the binary version of the hash
  627. memcpy(strBinaryHPwKey.Buffer,
  628. (pUserCreds->strDigestHash.Buffer + usHashOffset),
  629. MD5_HASH_BYTESIZE);
  630. strBinaryHPwKey.Length = MD5_HASH_BYTESIZE;
  631. // all zero for hash indicates invalid hash calculated
  632. for (i=0; i < (int)strBinaryHPwKey.Length; i++)
  633. {
  634. if (strBinaryHPwKey.Buffer[i])
  635. {
  636. fValidHash = TRUE;
  637. break;
  638. }
  639. }
  640. if (fValidHash == FALSE)
  641. {
  642. // This is not a defined hash
  643. DebugLog((DEB_ERROR, "DigestCalcHA1: Invalid hash selected - not defined\n"));
  644. Status = SEC_E_NO_CREDENTIALS;
  645. goto CleanUp;
  646. }
  647. if (fSASLMode == TRUE)
  648. {
  649. // SASL mode keeps the Password hash in binary form
  650. Status = StringDuplicate(&strHPwKey, &strBinaryHPwKey);
  651. if (!NT_SUCCESS(Status))
  652. {
  653. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  654. Status = SEC_E_INSUFFICIENT_MEMORY;
  655. goto CleanUp;
  656. }
  657. }
  658. else
  659. {
  660. Status = StringAllocate(&strHPwKey, MD5_HASH_HEX_SIZE);
  661. if (!NT_SUCCESS(Status))
  662. {
  663. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  664. Status = SEC_E_INSUFFICIENT_MEMORY;
  665. goto CleanUp;
  666. }
  667. // HTTP mode needs to have HEX() version of password hash - RFC text is correct
  668. BinToHex((LPBYTE)strBinaryHPwKey.Buffer, MD5_HASH_BYTESIZE, strHPwKey.Buffer);
  669. strHPwKey.Length = MD5_HASH_HEX_SIZE; // Do not count the NULL at the end
  670. }
  671. #if DBG
  672. if (fSASLMode == TRUE)
  673. {
  674. STRING strTempPwKey;
  675. ZeroMemory(&strTempPwKey, sizeof(strTempPwKey));
  676. MyPrintBytes(strHPwKey.Buffer, strHPwKey.Length, &strTempPwKey);
  677. DebugLog((DEB_TRACE, "DigestCalcHA1: SASL Pre-Calc H(%wZ:%wZ:************) is %Z\n",
  678. &pUserCreds->ustrUsername, &pUserCreds->ustrDomain, &strTempPwKey));
  679. StringFree(&strTempPwKey);
  680. }
  681. else
  682. {
  683. DebugLog((DEB_TRACE, "DigestCalcHA1: HTTP Pre-Calc H(%wZ:%wZ:************) is %Z\n",
  684. &pUserCreds->ustrUsername, &pUserCreds->ustrDomain, &strHPwKey));
  685. }
  686. #endif
  687. }
  688. else if (pUserCreds->fIsValidPasswd == TRUE)
  689. {
  690. // copy over the passwd and decrypt if necessary
  691. Status = UnicodeStringDuplicatePassword(&ustrTempPasswd, &(pUserCreds->ustrPasswd));
  692. if (!NT_SUCCESS(Status))
  693. {
  694. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in dup password, status 0x%0x\n", Status ));
  695. goto CleanUp;
  696. }
  697. if ((pUserCreds->fIsEncryptedPasswd == TRUE) && (ustrTempPasswd.MaximumLength != 0))
  698. {
  699. g_LsaFunctions->LsaUnprotectMemory(ustrTempPasswd.Buffer, (ULONG)(ustrTempPasswd.MaximumLength));
  700. }
  701. // Need to encode the password for hash calculations
  702. // We have the cleartext password in ustrTempPasswd,
  703. // username in pContext->ustrAccountname,
  704. // realm in pContext->ustrDomain
  705. // Could do some code size optimization here in the future to shorten this up
  706. if (pDigest->typeCharset == UTF_8)
  707. {
  708. // First check if OK to encode in ISO 8859-1, if not then use UTF-8
  709. // All characters must be within ISO 8859-1 Character set else fail
  710. fDefChars = FALSE;
  711. Status = EncodeUnicodeString(&pUserCreds->ustrUsername, CP_8859_1, &strUsername, &fDefChars);
  712. if (!NT_SUCCESS(Status))
  713. {
  714. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding username\n"));
  715. Status = SEC_E_INSUFFICIENT_MEMORY;
  716. goto CleanUp;
  717. }
  718. if (fDefChars == TRUE)
  719. {
  720. DebugLog((DEB_TRACE, "DigestCalcHA1: Can not encode Username in 8859-1, use UTF-8\n"));
  721. StringFree(&strUsername);
  722. Status = EncodeUnicodeString(&pUserCreds->ustrUsername, CP_UTF8, &strUsername, NULL);
  723. if (!NT_SUCCESS(Status))
  724. {
  725. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding username\n"));
  726. Status = SEC_E_INSUFFICIENT_MEMORY;
  727. goto CleanUp;
  728. }
  729. }
  730. fDefChars = FALSE;
  731. Status = EncodeUnicodeString(&pUserCreds->ustrDomain, CP_8859_1, &strRealm, &fDefChars);
  732. if (!NT_SUCCESS(Status))
  733. {
  734. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding realm\n"));
  735. Status = SEC_E_INSUFFICIENT_MEMORY;
  736. goto CleanUp;
  737. }
  738. if (fDefChars == TRUE)
  739. {
  740. DebugLog((DEB_TRACE, "DigestCalcHA1: Can not encode realm in 8859-1, use UTF-8\n"));
  741. StringFree(&strRealm);
  742. Status = EncodeUnicodeString(&pUserCreds->ustrDomain, CP_UTF8, &strRealm, NULL);
  743. if (!NT_SUCCESS(Status))
  744. {
  745. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding realm\n"));
  746. Status = SEC_E_INSUFFICIENT_MEMORY;
  747. goto CleanUp;
  748. }
  749. }
  750. fDefChars = FALSE;
  751. Status = EncodeUnicodeString(&ustrTempPasswd, CP_8859_1, &strPasswd, &fDefChars);
  752. if (!NT_SUCCESS(Status))
  753. {
  754. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding passwd\n"));
  755. Status = SEC_E_INSUFFICIENT_MEMORY;
  756. goto CleanUp;
  757. }
  758. if (fDefChars == TRUE)
  759. {
  760. DebugLog((DEB_TRACE, "DigestCalcHA1: Can not encode password in 8859-1, use UTF-8\n"));
  761. if (strPasswd.Buffer && strPasswd.MaximumLength)
  762. {
  763. ZeroMemory(strPasswd.Buffer, strPasswd.MaximumLength);
  764. }
  765. StringFree(&strPasswd);
  766. Status = EncodeUnicodeString(&ustrTempPasswd, CP_UTF8, &strPasswd, NULL);
  767. if (!NT_SUCCESS(Status))
  768. {
  769. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding passwd\n"));
  770. Status = SEC_E_INSUFFICIENT_MEMORY;
  771. goto CleanUp;
  772. }
  773. }
  774. }
  775. else
  776. {
  777. // All characters must be within ISO 8859-1 Character set else fail
  778. Status = EncodeUnicodeString(&pUserCreds->ustrUsername, CP_8859_1, &strUsername, &fDefChars);
  779. if (!NT_SUCCESS(Status))
  780. {
  781. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding username\n"));
  782. Status = SEC_E_INSUFFICIENT_MEMORY;
  783. goto CleanUp;
  784. }
  785. if (fDefChars == TRUE)
  786. {
  787. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding username in ISO 8859-1\n"));
  788. Status = STATUS_UNMAPPABLE_CHARACTER;
  789. goto CleanUp;
  790. }
  791. Status = EncodeUnicodeString(&pUserCreds->ustrDomain, CP_8859_1, &strRealm, &fDefChars);
  792. if (!NT_SUCCESS(Status))
  793. {
  794. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding realm\n"));
  795. Status = SEC_E_INSUFFICIENT_MEMORY;
  796. goto CleanUp;
  797. }
  798. if (fDefChars == TRUE)
  799. {
  800. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding realm in ISO 8859-1\n"));
  801. Status = STATUS_UNMAPPABLE_CHARACTER;
  802. goto CleanUp;
  803. }
  804. Status = EncodeUnicodeString(&ustrTempPasswd, CP_8859_1, &strPasswd, &fDefChars);
  805. if (!NT_SUCCESS(Status))
  806. {
  807. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding passwd\n"));
  808. Status = SEC_E_INSUFFICIENT_MEMORY;
  809. goto CleanUp;
  810. }
  811. if (fDefChars == TRUE)
  812. {
  813. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding passwd in ISO 8859-1\n"));
  814. Status = STATUS_UNMAPPABLE_CHARACTER;
  815. goto CleanUp;
  816. }
  817. DebugLog((DEB_TRACE, "DigestCalcHA1: Username, Realm, Password encoded in ISO 8859-1\n"));
  818. }
  819. if (!strUsername.Length)
  820. {
  821. DebugLog((DEB_ERROR, "DigestCalcHA1: Must have non-zero length username\n"));
  822. Status = STATUS_INVALID_PARAMETER;
  823. goto CleanUp;
  824. }
  825. // Calculate H(A1) based on Algorithm type
  826. // Auth is not specified or "MD5"
  827. // Use H(A1Base) = H(username-value:realm-value:passwd)
  828. if (fSASLMode == TRUE)
  829. {
  830. Status = DigestHash7(&strUsername,
  831. &strRealm,
  832. &strPasswd,
  833. NULL, NULL, NULL, NULL,
  834. FALSE, &strHPwKey);
  835. }
  836. else
  837. {
  838. Status = DigestHash7(&strUsername,
  839. &strRealm,
  840. &strPasswd,
  841. NULL, NULL, NULL, NULL,
  842. TRUE, &strHPwKey);
  843. }
  844. if (!NT_SUCCESS(Status))
  845. {
  846. DebugLog((DEB_ERROR, "DigestCalcHA1:DigestCalcHA1 H(PwKey) failed : 0x%x\n", Status));
  847. goto CleanUp;
  848. }
  849. #if DBG
  850. if (fSASLMode == TRUE)
  851. {
  852. STRING strTempPwKey;
  853. ZeroMemory(&strTempPwKey, sizeof(strTempPwKey));
  854. MyPrintBytes(strHPwKey.Buffer, strHPwKey.Length, &strTempPwKey);
  855. DebugLog((DEB_TRACE, "DigestCalcHA1: SASL Password Calc H(%Z:%Z:************) is %Z\n",
  856. &strUsername, &strRealm, &strTempPwKey));
  857. StringFree(&strTempPwKey);
  858. }
  859. else
  860. {
  861. DebugLog((DEB_TRACE, "DigestCalcHA1: HTTP Password Calc H(%Z:%Z:************) is %Z\n",
  862. &strUsername, &strRealm, &strHPwKey));
  863. }
  864. #endif
  865. }
  866. else
  867. {
  868. Status = SEC_E_NO_CREDENTIALS;
  869. DebugLog((DEB_ERROR, "DigestCalcHA1: No Pre-calc hash or password\n"));
  870. goto CleanUp;
  871. }
  872. // Check if using SASL then need to add in the AuthzID
  873. if (fSASLMode == TRUE)
  874. {
  875. // Set to use AuthzID otherwise keep the NULL
  876. // set only if AuthzID contains data
  877. if ((pDigest->refstrParam[MD5_AUTH_AUTHZID]).Length &&
  878. (pDigest->refstrParam[MD5_AUTH_AUTHZID]).Buffer)
  879. {
  880. pstrAuthzID = &(pDigest->refstrParam[MD5_AUTH_AUTHZID]);
  881. }
  882. }
  883. DebugLog((DEB_TRACE, "DigestCalcHA1: Algorithm type %d\n", pDigest->typeAlgorithm));
  884. // Now check if using MD5-SESS. We need to form
  885. // H(A1) = H( H(PwKey) : nonce : cnonce [: authzID])
  886. // otherwise simply set H(A1) = H(PwKey)
  887. if (pDigest->typeAlgorithm == MD5_SESS)
  888. {
  889. DebugLog((DEB_TRACE, "DigestCalcHA1: First client-server auth\n"));
  890. Status = DigestHash7(&strHPwKey,
  891. &(pDigest->refstrParam[MD5_AUTH_NONCE]),
  892. &(pDigest->refstrParam[MD5_AUTH_CNONCE]),
  893. pstrAuthzID,
  894. NULL, NULL, NULL,
  895. TRUE, &(pDigest->strSessionKey));
  896. if (!NT_SUCCESS(Status))
  897. {
  898. DebugLog((DEB_ERROR, "DigestCalcHA1: SessionKey failed : 0x%x\n", Status));
  899. goto CleanUp;
  900. }
  901. }
  902. else
  903. { // Keep SessionKey = H(PwKey) for Algoithm = MD5
  904. memcpy(pDigest->strSessionKey.Buffer, strHPwKey.Buffer, MD5_HASH_HEX_SIZE);
  905. pDigest->strSessionKey.Length = MD5_HASH_HEX_SIZE; // Do not count the NULL terminator
  906. }
  907. DebugLog((DEB_TRACE, "DigestCalcHA1: SessionKey is %Z\n", &(pDigest->strSessionKey)));
  908. Status = STATUS_SUCCESS;
  909. CleanUp:
  910. StringFree(&strBinaryHPwKey);
  911. StringFree(&strHPwKey);
  912. StringFree(&strHA0Base);
  913. StringFree(&strHA0);
  914. if (strPasswd.Buffer && strPasswd.MaximumLength)
  915. {
  916. ZeroMemory(strPasswd.Buffer, strPasswd.MaximumLength);
  917. }
  918. StringFree(&strPasswd);
  919. StringFree(&strUsername);
  920. StringFree(&strRealm);
  921. if (ustrTempPasswd.Buffer && ustrTempPasswd.MaximumLength)
  922. { // Zero out password info just to be safe
  923. ZeroMemory(ustrTempPasswd.Buffer, ustrTempPasswd.MaximumLength);
  924. }
  925. UnicodeStringFree(&ustrTempPasswd);
  926. DebugLog((DEB_TRACE_FUNC, "DigestCalcHA1: Leaving\n"));
  927. return(Status);
  928. }
  929. //+--------------------------------------------------------------------
  930. //
  931. // Function: DigestHash7
  932. //
  933. // Synopsis: Hash and Encode 7 STRINGS SOut = Hex(H(S1 S2 S3 S4 S5 S6 S7))
  934. //
  935. // Effects:
  936. //
  937. // Arguments: pS1,...,pS6 - STRINGS to hash, pS1 must be specified
  938. // fHexOut - perform a Hex operation on output
  939. // pSOut - STRING to hold Hex Encoded Hash
  940. //
  941. // Returns: STATUS_SUCCESS for normal completion
  942. //
  943. // Notes: pSOut->MaximumLength must be atleast (MD5_HASH_BYTESIZE (or MD5_HASH_HEX_SIZE) + sizeof(NULL))
  944. // Any pS# args which are NULL are skipped
  945. // if pS# is not NULL
  946. // Previously checked that pS# is non-zero length strings
  947. // You most likely want Sx->Length = strlen(Sx) so as not to include NULL
  948. // This function combines operations like H(S1 S2 S3), H(S1 S2 S3 S4 S5) ....
  949. // It is assumed that the char ':' is to be included getween Sn and Sn+1
  950. //
  951. //
  952. //---------------------------------------------------------------------
  953. NTSTATUS NTAPI
  954. DigestHash7(
  955. IN PSTRING pS1,
  956. IN PSTRING pS2,
  957. IN PSTRING pS3,
  958. IN PSTRING pS4,
  959. IN PSTRING pS5,
  960. IN PSTRING pS6,
  961. IN PSTRING pS7,
  962. IN BOOL fHexOut,
  963. OUT PSTRING pSOut)
  964. {
  965. NTSTATUS Status = STATUS_SUCCESS;
  966. HCRYPTHASH hHash = NULL;
  967. BYTE bHashData[MD5_HASH_BYTESIZE];
  968. DWORD cbHashData = MD5_HASH_BYTESIZE;
  969. USHORT usSizeRequired = 0;
  970. DebugLog((DEB_TRACE_FUNC, "DigestHash7: Entering \n"));
  971. ASSERT(pSOut);
  972. if (fHexOut == TRUE)
  973. {
  974. usSizeRequired = MD5_HASH_HEX_SIZE;
  975. }
  976. else
  977. {
  978. usSizeRequired = MD5_HASH_BYTESIZE;
  979. }
  980. // Check if output is proper size or allocate one
  981. if (!pSOut->Buffer)
  982. {
  983. Status = StringAllocate(pSOut, usSizeRequired);
  984. if (!NT_SUCCESS(Status))
  985. {
  986. DebugLog((DEB_ERROR, "DigestHash7: No Memory\n"));
  987. Status = SEC_E_INSUFFICIENT_MEMORY;
  988. goto CleanUp;
  989. }
  990. }
  991. else
  992. {
  993. if (pSOut->MaximumLength < (usSizeRequired + 1))
  994. {
  995. // Output is not large enough to hold Hex(Hash)
  996. DebugLog((DEB_ERROR, "DigestHash7: Output buffer too small\n"));
  997. Status = STATUS_BUFFER_TOO_SMALL;
  998. goto CleanUp;
  999. }
  1000. }
  1001. if ( !CryptCreateHash( g_hCryptProv,
  1002. CALG_MD5,
  1003. 0,
  1004. 0,
  1005. &hHash ) )
  1006. {
  1007. DebugLog((DEB_ERROR, "DigestHash7: CryptCreateHash failed : 0x%lx\n", GetLastError()));
  1008. Status = STATUS_ENCRYPTION_FAILED;
  1009. goto CleanUp;
  1010. }
  1011. if (pS1)
  1012. {
  1013. if ( !CryptHashData( hHash,
  1014. (const unsigned char *)pS1->Buffer,
  1015. pS1->Length,
  1016. 0 ) )
  1017. {
  1018. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1019. CryptDestroyHash( hHash );
  1020. hHash = NULL;
  1021. Status = STATUS_ENCRYPTION_FAILED;
  1022. goto CleanUp;
  1023. }
  1024. }
  1025. if (pS2)
  1026. {
  1027. if ( !CryptHashData( hHash,
  1028. (const unsigned char *)pbSeparator,
  1029. COLONSTR_LEN,
  1030. 0 ) )
  1031. {
  1032. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1033. CryptDestroyHash( hHash );
  1034. hHash = NULL;
  1035. Status = STATUS_ENCRYPTION_FAILED;
  1036. goto CleanUp;
  1037. }
  1038. if ( !CryptHashData( hHash,
  1039. (const unsigned char *)pS2->Buffer,
  1040. pS2->Length,
  1041. 0 ) )
  1042. {
  1043. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1044. CryptDestroyHash( hHash );
  1045. hHash = NULL;
  1046. Status = STATUS_ENCRYPTION_FAILED;
  1047. goto CleanUp;
  1048. }
  1049. }
  1050. if (pS3)
  1051. {
  1052. (void)CryptHashData( hHash,
  1053. (const unsigned char *)pbSeparator,
  1054. COLONSTR_LEN,
  1055. 0 );
  1056. if ( !CryptHashData( hHash,
  1057. (const unsigned char *)pS3->Buffer,
  1058. pS3->Length,
  1059. 0 ) )
  1060. {
  1061. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1062. CryptDestroyHash( hHash );
  1063. hHash = NULL;
  1064. Status = STATUS_ENCRYPTION_FAILED;
  1065. goto CleanUp;
  1066. }
  1067. }
  1068. if (pS4)
  1069. {
  1070. (void)CryptHashData( hHash,
  1071. (const unsigned char *)pbSeparator,
  1072. COLONSTR_LEN,
  1073. 0 );
  1074. if ( !CryptHashData( hHash,
  1075. (const unsigned char *)pS4->Buffer,
  1076. pS4->Length,
  1077. 0 ) )
  1078. {
  1079. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1080. CryptDestroyHash( hHash );
  1081. hHash = NULL;
  1082. Status = STATUS_ENCRYPTION_FAILED;
  1083. goto CleanUp;
  1084. }
  1085. }
  1086. if (pS5)
  1087. {
  1088. (void)CryptHashData( hHash,
  1089. (const unsigned char *)pbSeparator,
  1090. COLONSTR_LEN,
  1091. 0 );
  1092. if ( !CryptHashData( hHash,
  1093. (const unsigned char *)pS5->Buffer,
  1094. pS5->Length,
  1095. 0 ) )
  1096. {
  1097. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1098. CryptDestroyHash( hHash );
  1099. hHash = NULL;
  1100. Status = STATUS_ENCRYPTION_FAILED;
  1101. goto CleanUp;
  1102. }
  1103. }
  1104. if (pS6)
  1105. {
  1106. (void)CryptHashData( hHash,
  1107. (const unsigned char *)pbSeparator,
  1108. COLONSTR_LEN,
  1109. 0 );
  1110. if ( !CryptHashData( hHash,
  1111. (const unsigned char *)pS6->Buffer,
  1112. pS6->Length,
  1113. 0 ) )
  1114. {
  1115. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1116. CryptDestroyHash( hHash );
  1117. hHash = NULL;
  1118. Status = STATUS_ENCRYPTION_FAILED;
  1119. goto CleanUp;
  1120. }
  1121. }
  1122. if (pS7)
  1123. {
  1124. (void)CryptHashData( hHash,
  1125. (const unsigned char *)pbSeparator,
  1126. COLONSTR_LEN,
  1127. 0 );
  1128. if ( !CryptHashData( hHash,
  1129. (const unsigned char *)pS7->Buffer,
  1130. pS7->Length,
  1131. 0 ) )
  1132. {
  1133. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1134. CryptDestroyHash( hHash );
  1135. hHash = NULL;
  1136. Status = STATUS_ENCRYPTION_FAILED;
  1137. goto CleanUp;
  1138. }
  1139. }
  1140. if ( !CryptGetHashParam( hHash,
  1141. HP_HASHVAL,
  1142. bHashData,
  1143. &cbHashData,
  1144. 0 ) )
  1145. {
  1146. DebugLog((DEB_ERROR, "DigestHash7: CryptGetHashParam failed : 0x%lx\n", GetLastError()));
  1147. CryptDestroyHash( hHash );
  1148. hHash = NULL;
  1149. Status = STATUS_ENCRYPTION_FAILED;
  1150. goto CleanUp;
  1151. }
  1152. CryptDestroyHash( hHash );
  1153. hHash = NULL;
  1154. ASSERT(cbHashData == MD5_HASH_BYTESIZE);
  1155. if (fHexOut == TRUE)
  1156. {
  1157. BinToHex((LPBYTE)&bHashData, cbHashData, pSOut->Buffer);
  1158. pSOut->Length = MD5_HASH_HEX_SIZE; // Do not count the NULL at the end
  1159. }
  1160. else
  1161. {
  1162. memcpy(pSOut->Buffer, &bHashData, cbHashData);
  1163. pSOut->Length = MD5_HASH_BYTESIZE; // Do not count the NULL at the end
  1164. }
  1165. CleanUp:
  1166. DebugLog((DEB_TRACE_FUNC, "DigestHash7: Leaving Status 0x%x\n", Status));
  1167. return(Status);
  1168. }
  1169. // Blob creation/extraction for GenericPassthrough Messages
  1170. //+--------------------------------------------------------------------
  1171. //
  1172. // Function: BlobEncodeRequest
  1173. //
  1174. // Synopsis: Encode the Digest Access Parameters fields into a BYTE Buffer
  1175. //
  1176. // Effects: Creates a Buffer allocation which calling function
  1177. // is responsible to delete with call to BlobFreeRequest()
  1178. //
  1179. // Arguments: pDigest - pointer to digest access data fields
  1180. //
  1181. // Returns:
  1182. //
  1183. // Notes: STATUS_SUCCESS for normal completion
  1184. //
  1185. //
  1186. //---------------------------------------------------------------------
  1187. NTSTATUS NTAPI
  1188. BlobEncodeRequest(
  1189. PDIGEST_PARAMETER pDigest,
  1190. OUT BYTE **ppOutBuffer,
  1191. OUT USHORT *cbOutBuffer
  1192. )
  1193. {
  1194. NTSTATUS Status = STATUS_SUCCESS;
  1195. DebugLog((DEB_TRACE_FUNC, "BlobEncodeRequest: Entering\n"));
  1196. USHORT cbBuffer = 0;
  1197. BYTE *pBuffer = NULL;
  1198. char *pch = NULL;
  1199. PDIGEST_BLOB_REQUEST pHeader;
  1200. int i = 0;
  1201. USHORT cbValue = 0;
  1202. // Now figure out how many bytes needed to hold field-value NULL terminated
  1203. for (i=0, cbBuffer = 0;i < DIGEST_BLOB_VALUES;i++)
  1204. {
  1205. if (pDigest->refstrParam[i].Buffer && pDigest->refstrParam[i].Length)
  1206. { // may be able to just count str.length
  1207. cbBuffer += (USHORT)strlencounted(pDigest->refstrParam[i].Buffer, pDigest->refstrParam[i].MaximumLength);
  1208. }
  1209. }
  1210. cbBuffer += (DIGEST_BLOB_VALUES * sizeof(char)); // Account for the separating/terminating NULLs
  1211. // Now add in space for the DSCrackName accountname and domain
  1212. if (pDigest->ustrCrackedAccountName.Buffer && pDigest->ustrCrackedAccountName.Length)
  1213. {
  1214. cbBuffer += (USHORT)(ustrlencounted((const short *)pDigest->ustrCrackedAccountName.Buffer,
  1215. pDigest->ustrCrackedAccountName.MaximumLength) * sizeof(WCHAR));
  1216. }
  1217. if (pDigest->ustrCrackedDomain.Buffer && pDigest->ustrCrackedDomain.Length)
  1218. {
  1219. cbBuffer += (USHORT)(ustrlencounted((const short *)pDigest->ustrCrackedDomain.Buffer,
  1220. pDigest->ustrCrackedDomain.MaximumLength) * sizeof(WCHAR));
  1221. }
  1222. cbBuffer += (2 * sizeof(WCHAR)); // Account for the separating/terminating NULLs
  1223. cbValue = cbBuffer + (sizeof(DIGEST_BLOB_REQUEST)); // there will be one extra byte
  1224. if (!(pBuffer = (BYTE *)DigestAllocateMemory(cbValue)))
  1225. {
  1226. DebugLog((DEB_ERROR, "BlobEncodeRequest out of memory\n"));
  1227. Status = SEC_E_INSUFFICIENT_MEMORY;
  1228. goto CleanUp;
  1229. }
  1230. DebugLog((DEB_TRACE, "BlobEncodeRequest using %d bytes\n", cbValue));
  1231. *cbOutBuffer = cbValue; // Return number of bytes we are using for the encoding
  1232. // Zero out memory to autoplace the separating NULLs
  1233. memset(pBuffer, 0, cbValue);
  1234. // Now fill in the information
  1235. pHeader = (PDIGEST_BLOB_REQUEST)pBuffer;
  1236. pHeader->MessageType = VERIFY_DIGEST_MESSAGE;
  1237. pHeader->version = DIGEST_BLOB_VERSION;
  1238. pHeader->digest_type = (USHORT)pDigest->typeDigest;
  1239. pHeader->qop_type = (USHORT)pDigest->typeQOP;
  1240. pHeader->alg_type = (USHORT)pDigest->typeAlgorithm;
  1241. pHeader->charset_type = (USHORT)pDigest->typeCharset;
  1242. pHeader->name_format = (USHORT)pDigest->typeName; // Format of the username
  1243. pHeader->cbCharValues = cbBuffer;
  1244. pHeader->cbBlobSize = cbValue; // cbCharValues + charvalues
  1245. // Simply copy over the first DIGEST_BLOB_VALUES that are arranged to be
  1246. for (i = 0,pch = &(pHeader->cCharValues); i < DIGEST_BLOB_VALUES;i++)
  1247. {
  1248. // Make sure that there is valid data to get length from
  1249. if (pDigest->refstrParam[i].Buffer && pDigest->refstrParam[i].Length)
  1250. {
  1251. cbValue = (USHORT)strlencounted(pDigest->refstrParam[i].Buffer, pDigest->refstrParam[i].MaximumLength);
  1252. // dont use .length since may include multiple NULLS
  1253. memcpy(pch, pDigest->refstrParam[i].Buffer, cbValue);
  1254. }
  1255. else
  1256. cbValue = 0;
  1257. pch += (cbValue + 1); // This will leave one NULL at end of field-value
  1258. }
  1259. // Now write out any results from DSCrackName
  1260. if (pDigest->ustrCrackedAccountName.Buffer && pDigest->ustrCrackedAccountName.Length)
  1261. {
  1262. cbValue = (USHORT)(ustrlencounted((const short *)pDigest->ustrCrackedAccountName.Buffer,
  1263. pDigest->ustrCrackedAccountName.MaximumLength) * sizeof(WCHAR));
  1264. memcpy(pch, pDigest->ustrCrackedAccountName.Buffer, cbValue);
  1265. }
  1266. else
  1267. {
  1268. cbValue = 0;
  1269. }
  1270. pch += (cbValue + sizeof(WCHAR)); // This will leave one WCHAR NULL at end of CrackedAccountName
  1271. if (pDigest->ustrCrackedDomain.Buffer && pDigest->ustrCrackedDomain.Length)
  1272. {
  1273. cbValue = (USHORT)(ustrlencounted((const short *)pDigest->ustrCrackedDomain.Buffer,
  1274. pDigest->ustrCrackedDomain.MaximumLength) * sizeof(WCHAR));
  1275. memcpy(pch, pDigest->ustrCrackedDomain.Buffer, cbValue);
  1276. }
  1277. else
  1278. {
  1279. cbValue = 0;
  1280. }
  1281. pch += (cbValue + sizeof(WCHAR)); // This will leave one WCHAR NULL at end of CrackedAccountName
  1282. *ppOutBuffer = pBuffer; // Pass off memory back to calling routine
  1283. DebugLog((DEB_TRACE, "BlobEncodeRequest: message_type 0x%x, version %d, CharValues %d, BlobSize %d\n",
  1284. pHeader->digest_type, pHeader->version, pHeader->cbCharValues, pHeader->cbBlobSize));
  1285. CleanUp:
  1286. DebugLog((DEB_TRACE_FUNC, "BlobEncodeRequest: Leaving Status 0x%x\n", Status));
  1287. return(Status);
  1288. }
  1289. //+--------------------------------------------------------------------
  1290. //
  1291. // Function: BlobDecodeRequest
  1292. //
  1293. // Synopsis: Decode the Digest Access Parameters fields from a BYTE Buffer
  1294. //
  1295. // Arguments: pBuffer - pointer to BlobEncodeRequestd buffer as input
  1296. // pDigest - pointer to Digest parameter struct to set STRINGS
  1297. // to point within pBuffer. No string memory is allocated
  1298. //
  1299. // Returns: NTSTATUS
  1300. //
  1301. // Notes:
  1302. // Currently only processes a single version of the packet. Check MessageType
  1303. // and version number if new message types are supported on the DC.
  1304. //
  1305. //---------------------------------------------------------------------
  1306. NTSTATUS NTAPI BlobDecodeRequest(
  1307. IN BYTE *pBuffer,
  1308. PDIGEST_PARAMETER pDigest
  1309. )
  1310. {
  1311. NTSTATUS Status = STATUS_SUCCESS;
  1312. DIGEST_BLOB_REQUEST Header;
  1313. PDIGEST_BLOB_REQUEST pHeader;
  1314. char *pch = NULL;
  1315. USHORT sLen = 0;
  1316. int i = 0; // counter
  1317. BOOL fKnownFormat = FALSE;
  1318. USHORT sMaxRead = 0;
  1319. PWCHAR pusTemp = NULL;
  1320. PWCHAR pusLoc = NULL;
  1321. PUSHORT pusTempLoc = NULL;
  1322. USHORT usCnt = 0;
  1323. DebugLog((DEB_TRACE_FUNC, "BlobDecodeRequest: Entering\n"));
  1324. if (!pBuffer || !pDigest)
  1325. {
  1326. DebugLog((DEB_ERROR, "BlobDecodeRequest: Invalid parameter\n"));
  1327. Status = STATUS_INVALID_PARAMETER;
  1328. goto CleanUp;
  1329. }
  1330. // Copy over the header for byte alignment
  1331. memcpy((char *)&Header, (char *)pBuffer, sizeof(Header));
  1332. DebugLog((DEB_TRACE, "BlobDecodeRequest: message_type %lu version %d, CharValues %d, BlobSize %d\n",
  1333. Header.MessageType, Header.version, Header.cbCharValues, Header.cbBlobSize));
  1334. // Process the encoded message - use only the known MessageTypes and versions here on the DC
  1335. // This allows for expansion of protocols supported in the future
  1336. if ((Header.MessageType == VERIFY_DIGEST_MESSAGE) && (Header.version == DIGEST_BLOB_VERSION))
  1337. {
  1338. fKnownFormat = TRUE;
  1339. DebugLog((DEB_TRACE, "BlobDecodeRequest: Blob from server known type and version\n"));
  1340. }
  1341. if (!fKnownFormat)
  1342. {
  1343. DebugLog((DEB_ERROR, "BlobDecodeRequest: Not supported MessageType/Version\n"));
  1344. Status = STATUS_INVALID_PARAMETER;
  1345. goto CleanUp;
  1346. }
  1347. pDigest->typeDigest = (DIGEST_TYPE)Header.digest_type;
  1348. pDigest->typeQOP = (QOP_TYPE)Header.qop_type;
  1349. pDigest->typeAlgorithm = (ALGORITHM_TYPE)Header.alg_type;
  1350. pDigest->typeCharset = (CHARSET_TYPE)Header.charset_type;
  1351. pDigest->typeName = (NAMEFORMAT_TYPE)Header.name_format;
  1352. DebugLog((DEB_TRACE, "BlobDecodeRequest: typeDigest 0x%x, typeQOP %d, typeAlgorithm %d, typeCharset %d NameFormat %d\n",
  1353. pDigest->typeDigest, pDigest->typeQOP, pDigest->typeAlgorithm, pDigest->typeCharset, pDigest->typeName));
  1354. pHeader = (PDIGEST_BLOB_REQUEST)pBuffer;
  1355. pch = &(pHeader->cCharValues); // strings start on last char of struct
  1356. sMaxRead = Header.cbCharValues;
  1357. for (i = 0; i < DIGEST_BLOB_VALUES;i++)
  1358. {
  1359. sLen = (USHORT)strlencounted(pch, sMaxRead);
  1360. if (!sLen)
  1361. {
  1362. // Null String no value skip to next
  1363. pch++;
  1364. sMaxRead--;
  1365. }
  1366. else
  1367. { // Simple check to make sure that we do not copy way too much
  1368. if (sLen < (Header.cbCharValues))
  1369. {
  1370. DebugLog((DEB_TRACE, "BlobDecodeRequest: Setting Digest[%d] = %s\n", i, pch));
  1371. pDigest->refstrParam[i].Buffer = pch;
  1372. pDigest->refstrParam[i].Length = sLen;
  1373. pDigest->refstrParam[i].MaximumLength = sLen+1;
  1374. pch += (sLen + 1); // skip over field-value and NULL
  1375. sMaxRead -= (sLen + 1);
  1376. }
  1377. else
  1378. {
  1379. // This indicates failed NULL separators in BlobData
  1380. // Really should not happen unless encoded wrong
  1381. Status = STATUS_INTERNAL_DB_CORRUPTION;
  1382. memset(pDigest, 0, sizeof(DIGEST_PARAMETER)); // scrubbed all info
  1383. DebugLog((DEB_ERROR, "BlobDecodeRequest: NULL separator missing\n"));
  1384. goto CleanUp;
  1385. }
  1386. }
  1387. }
  1388. if (pDigest->typeName != NAMEFORMAT_UNKNOWN)
  1389. {
  1390. // Read in the values that DSCrackName on the server found out
  1391. // Need to place on SHORT boundary for Unicode string processing
  1392. usCnt = sMaxRead + (2 * sizeof(WCHAR));
  1393. pusTemp = (PWCHAR)DigestAllocateMemory(usCnt); // Force a NULL terminator just to be safe
  1394. if (!pusTemp)
  1395. {
  1396. Status = STATUS_NO_MEMORY;
  1397. DebugLog((DEB_ERROR, "BlobDecodeRequest: Memory Alloc Error\n"));
  1398. goto CleanUp;
  1399. }
  1400. // Format will be Unicode_account_name NULL Unicode_domain_name NULL [NULL NULL]
  1401. memcpy((PCHAR)pusTemp, pch, sMaxRead);
  1402. // Read out the two unicode strings
  1403. Status = UnicodeStringWCharDuplicate(&(pDigest->ustrCrackedAccountName), pusTemp);
  1404. if (!NT_SUCCESS(Status))
  1405. {
  1406. DebugLog((DEB_ERROR,"BlobDecodeRequest: Failed to duplicate Account Name: 0x%x\n",Status));
  1407. goto CleanUp;
  1408. }
  1409. pusLoc = pusTemp + (1 + (pDigest->ustrCrackedAccountName.Length / sizeof(WCHAR))); // Skip NULL
  1410. Status = UnicodeStringWCharDuplicate(&(pDigest->ustrCrackedDomain), pusLoc);
  1411. if (!NT_SUCCESS(Status))
  1412. {
  1413. DebugLog((DEB_ERROR,"BlobDecodeRequest: Failed to duplicate Domain Name: 0x%x\n",Status));
  1414. goto CleanUp;
  1415. }
  1416. DebugLog((DEB_TRACE,"BlobDecodeRequest: Cracked Account %wZ Domain %wZ\n",
  1417. &(pDigest->ustrCrackedAccountName),
  1418. &(pDigest->ustrCrackedDomain)));
  1419. }
  1420. CleanUp:
  1421. DebugLog((DEB_TRACE_FUNC, "Leaving BlobEncodeRequest 0x%x\n", Status));
  1422. if (pusTemp)
  1423. {
  1424. DigestFreeMemory(pusTemp);
  1425. pusTemp = NULL;
  1426. }
  1427. return(Status);
  1428. }
  1429. // Free BYTE Buffer from BlobEncodeRequest
  1430. VOID NTAPI BlobFreeRequest(
  1431. BYTE *pBuffer
  1432. )
  1433. {
  1434. if (pBuffer)
  1435. {
  1436. DigestFreeMemory(pBuffer);
  1437. }
  1438. return;
  1439. }