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.

2382 lines
79 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 = STATUS_SUCCESS;
  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->ustrCrackedAccountName);
  181. UnicodeStringFree(&pDigest->ustrCrackedDomain);
  182. UnicodeStringFree(&pDigest->ustrWorkstation);
  183. UnicodeStringFree(&pDigest->ustrTrustedForest);
  184. if (pDigest->pTrustSid)
  185. {
  186. #ifndef SECURITY_KERNEL
  187. MIDL_user_free(pDigest->pTrustSid);
  188. #else
  189. ASSERT(FALSE); // Kernel mode will never have Domain/forest trust informtion set
  190. #endif
  191. pDigest->pTrustSid = NULL;
  192. }
  193. // Release any directive storage
  194. // This was used to remove backslash encoding from directives
  195. for (i = 0; i < MD5_AUTH_LAST; i++)
  196. {
  197. StringFree(&(pDigest->strDirective[i]));
  198. }
  199. DebugLog((DEB_TRACE_FUNC, "Leaving DigestFree\n"));
  200. return(Status);
  201. }
  202. //+--------------------------------------------------------------------
  203. //
  204. // Function: DigestInit
  205. //
  206. // Synopsis: Initialize a DIGEST_PARAMETER structure
  207. //
  208. // Effects:
  209. //
  210. // Arguments: pDigest - pointer to digest access data fields
  211. //
  212. // Returns: STATUS_SUCCESS for normal completion
  213. //
  214. // Notes: This should be called when creating a DIGEST_PARAMTER object
  215. //
  216. //
  217. //---------------------------------------------------------------------
  218. NTSTATUS NTAPI
  219. DigestInit(
  220. IN PDIGEST_PARAMETER pDigest
  221. )
  222. {
  223. NTSTATUS Status = STATUS_SUCCESS;
  224. if (!pDigest)
  225. {
  226. return STATUS_INVALID_PARAMETER;
  227. }
  228. RtlZeroMemory(pDigest, sizeof(DIGEST_PARAMETER));
  229. // Now allocate the fixed length output buffers
  230. Status = StringAllocate(&(pDigest->strResponse), MD5_HASH_HEX_SIZE);
  231. if (!NT_SUCCESS(Status))
  232. {
  233. DebugLog((DEB_ERROR, "NTDigest:DigestCalculation No Memory\n"));
  234. Status = SEC_E_INSUFFICIENT_MEMORY;
  235. return(Status);
  236. }
  237. return(Status);
  238. }
  239. //+--------------------------------------------------------------------
  240. //
  241. // Function: DigestCalcChalRsp
  242. //
  243. // Synopsis: Perform Digest Access Calculation for ChallengeResponse
  244. //
  245. // Effects:
  246. //
  247. // Arguments: pDigest - pointer to digest access data fields
  248. // bIsChallenge - if TRUE then check Response provided (for HTTP Response)
  249. // - if FALSE then calculate Response (for HTTP Request)
  250. //
  251. // Returns: STATUS_SUCCESS for normal completion
  252. //
  253. // Notes: Called from DigestCalculation
  254. //
  255. //
  256. //---------------------------------------------------------------------
  257. NTSTATUS NTAPI
  258. DigestCalcChalRsp(IN PDIGEST_PARAMETER pDigest,
  259. IN PUSER_CREDENTIALS pUserCreds,
  260. BOOL IsChallenge)
  261. {
  262. NTSTATUS Status = STATUS_SUCCESS;
  263. STRING strHA2 = {0};
  264. STRING strReqDigest = {0}; // Final request digest access value
  265. STRING strcQOP = {0}; // String pointing to a constant CZ - no need to free up
  266. DebugLog((DEB_TRACE_FUNC, "DigestCalcChalRsp: Entering\n"));
  267. // Make sure that there is a Request-Digest to Compare to (IsChallenge TRUE) or
  268. // Set (IsChallenge FALSE)
  269. if (IsChallenge && (!(pDigest->refstrParam[MD5_AUTH_RESPONSE].Length)))
  270. {
  271. // Failed on a require field-value
  272. DebugLog((DEB_ERROR, "DigestCalcChalRsp: No ChallengeResponse\n"));
  273. Status = STATUS_INVALID_PARAMETER;
  274. return(Status);
  275. }
  276. // Initialize local variables
  277. Status = StringAllocate(&strHA2, MD5_HASH_HEX_SIZE);
  278. if (!NT_SUCCESS(Status))
  279. {
  280. DebugLog((DEB_ERROR, "DigestCalcChalRsp: No Memory\n"));
  281. Status = SEC_E_INSUFFICIENT_MEMORY;
  282. goto CleanUp;
  283. }
  284. Status = StringAllocate(&strReqDigest, MD5_HASH_HEX_SIZE);
  285. if (!NT_SUCCESS(Status))
  286. {
  287. DebugLog((DEB_ERROR, "DigestCalcChalRsp: No Memory\n"));
  288. Status = SEC_E_INSUFFICIENT_MEMORY;
  289. goto CleanUp;
  290. }
  291. // Establish which QOP utilized
  292. if (pDigest->typeQOP == AUTH_CONF)
  293. {
  294. RtlInitString(&strcQOP, AUTHCONFSTR);
  295. }
  296. else if (pDigest->typeQOP == AUTH_INT)
  297. {
  298. RtlInitString(&strcQOP, AUTHINTSTR);
  299. }
  300. else if (pDigest->typeQOP == AUTH)
  301. {
  302. RtlInitString(&strcQOP, AUTHSTR);
  303. }
  304. // Check if already calculated H(A1) the session key
  305. // Well for Algorithm=MD5 it is just H(username:realm:passwd)
  306. if (!(pDigest->strSessionKey.Length))
  307. {
  308. // No Session Key calculated yet - create one & store it
  309. #ifndef SECURITY_KERNEL
  310. DebugLog((DEB_TRACE, "DigestCalcChalRsp: No session key calculated, generate one\n"));
  311. Status = DigestCalcHA1(pDigest, pUserCreds);
  312. if (!NT_SUCCESS(Status))
  313. {
  314. goto CleanUp;
  315. }
  316. #else // SECURITY_KERNEL
  317. UNREFERENCED_PARAMETER(pUserCreds);
  318. DebugLog((DEB_ERROR, "DigestCalcChalRsp: No session key available in context\n"));
  319. Status = STATUS_NO_USER_SESSION_KEY;
  320. goto CleanUp;
  321. #endif // SECURITY_KERNEL
  322. }
  323. // We now have calculated H(A1)
  324. // Calculate H(A2)
  325. // For QOP unspecified or "auth" H(A2) = H( Method: URI)
  326. // For QOP Auth-int or Auth-conf H(A2) = H( Method: URI: H(entity-body))
  327. if ((pDigest->typeQOP == AUTH) || (pDigest->typeQOP == NO_QOP_SPECIFIED))
  328. {
  329. // Unspecified or Auth
  330. DebugLog((DEB_TRACE, "DigestCalcChalRsp: H(A2) using AUTH/Unspecified\n"));
  331. Status = DigestHash7(&(pDigest->refstrParam[MD5_AUTH_METHOD]),
  332. &(pDigest->refstrParam[MD5_AUTH_URI]),
  333. NULL, NULL, NULL, NULL, NULL,
  334. TRUE, &strHA2);
  335. if (!NT_SUCCESS(Status))
  336. {
  337. DebugLog((DEB_ERROR, "DigestCalcChalRsp H(A2) failed : 0x%x\n", Status));
  338. goto CleanUp;
  339. }
  340. }
  341. else
  342. {
  343. // Auth-int or Auth-conf
  344. DebugLog((DEB_TRACE, "DigestCalcChalRsp: H(A2) using AUTH-INT/CONF\n"));
  345. if (pDigest->refstrParam[MD5_AUTH_HENTITY].Length == 0)
  346. {
  347. Status = STATUS_INVALID_PARAMETER;
  348. DebugLog((DEB_ERROR, "DigestCalChalRsp HEntity Missing\n"));
  349. goto CleanUp;
  350. }
  351. Status = DigestHash7(&(pDigest->refstrParam[MD5_AUTH_METHOD]),
  352. &(pDigest->refstrParam[MD5_AUTH_URI]),
  353. &(pDigest->refstrParam[MD5_AUTH_HENTITY]),
  354. NULL, NULL, NULL, NULL,
  355. TRUE, &strHA2);
  356. if (!NT_SUCCESS(Status))
  357. {
  358. DebugLog((DEB_ERROR, "DigestCalcChalRsp H(A2) auth-int failed : 0x%x\n", Status));
  359. goto CleanUp;
  360. }
  361. }
  362. // We now have calculated H(A2)
  363. // Calculate Request-Digest
  364. // For QOP of Auth, Auth-int, Auth-conf Req-Digest = H( H(A1): nonce: nc: cnonce: qop: H(A2))
  365. // For QOP unspecified (old format) Req-Digest = H( H(A1): nonce: H(A2))
  366. if (pDigest->typeQOP != NO_QOP_SPECIFIED)
  367. {
  368. // Auth, Auth-int, Auth-conf
  369. if (pDigest->refstrParam[MD5_AUTH_NC].Length == 0)
  370. {
  371. Status = STATUS_INVALID_PARAMETER;
  372. DebugLog((DEB_ERROR, "DigestCalcChalRsp NC Missing\n"));
  373. goto CleanUp;
  374. }
  375. Status = DigestHash7(&(pDigest->strSessionKey),
  376. &(pDigest->refstrParam[MD5_AUTH_NONCE]),
  377. &(pDigest->refstrParam[MD5_AUTH_NC]),
  378. &(pDigest->refstrParam[MD5_AUTH_CNONCE]),
  379. &strcQOP,
  380. &strHA2, NULL,
  381. TRUE, &strReqDigest);
  382. if (!NT_SUCCESS(Status))
  383. {
  384. DebugLog((DEB_ERROR, "DigestCalcChalRsp Req-Digest failed : 0x%x\n", Status));
  385. goto CleanUp;
  386. }
  387. }
  388. else
  389. {
  390. // Unspecified backwards compat for RFC 2069
  391. Status = DigestHash7(&(pDigest->strSessionKey),
  392. &(pDigest->refstrParam[MD5_AUTH_NONCE]),
  393. &strHA2,
  394. NULL, NULL, NULL, NULL,
  395. TRUE, &strReqDigest);
  396. if (!NT_SUCCESS(Status))
  397. {
  398. DebugLog((DEB_ERROR, "DigestCalcChalRsp Req-Digest old format failed : 0x%x\n", Status));
  399. goto CleanUp;
  400. }
  401. }
  402. if (IsChallenge)
  403. {
  404. // We now have the Request-Digest so just compare to see if they match!
  405. if (!strncmp(pDigest->refstrParam[MD5_AUTH_RESPONSE].Buffer, strReqDigest.Buffer, 2*MD5_HASH_BYTESIZE))
  406. {
  407. DebugLog((DEB_TRACE, "DigestCalcChalRsp Request-Digest Matches!\n"));
  408. Status = STATUS_SUCCESS;
  409. }
  410. else
  411. {
  412. DebugLog((DEB_TRACE, "DigestCalcChalRsp Request-Digest FAILED.\n"));
  413. Status = STATUS_WRONG_PASSWORD;
  414. }
  415. }
  416. else
  417. {
  418. // We are to calculate the response-value so just set it (Hash Size + NULL)
  419. if (pDigest->strResponse.MaximumLength >= (MD5_HASH_HEX_SIZE + 1))
  420. {
  421. memcpy(pDigest->strResponse.Buffer, strReqDigest.Buffer, (MD5_HASH_HEX_SIZE + 1));
  422. pDigest->strResponse.Length = MD5_HASH_HEX_SIZE; // No Count NULL
  423. Status = STATUS_SUCCESS;
  424. }
  425. else
  426. {
  427. DebugLog((DEB_ERROR, "DigestCalcChalRsp Request-Digest Size too small.\n"));
  428. Status = STATUS_BUFFER_TOO_SMALL;
  429. }
  430. }
  431. CleanUp:
  432. StringFree(&strHA2);
  433. StringFree(&strReqDigest);
  434. DebugLog((DEB_TRACE_FUNC, "DigestCalcChalRsp: Leaving Status 0x%x\n", Status));
  435. return(Status);
  436. }
  437. #ifndef SECURITY_KERNEL
  438. //+--------------------------------------------------------------------
  439. //
  440. // Function: DigestDecodeDirectiveStrings
  441. //
  442. // Synopsis: Processed parsed digest auth message and fill in string values
  443. //
  444. // Effects:
  445. //
  446. // Arguments: pDigest - pointer to digest access data fields
  447. //
  448. // Returns: STATUS_SUCCESS for normal completion
  449. //
  450. // Notes:
  451. //
  452. //---------------------------------------------------------------------
  453. NTSTATUS NTAPI
  454. DigestDecodeDirectiveStrings(
  455. IN PDIGEST_PARAMETER pDigest
  456. )
  457. {
  458. NTSTATUS Status = STATUS_SUCCESS;
  459. DebugLog((DEB_TRACE_FUNC, "DigestDecodeDirectiveStrings Entering\n"));
  460. if (!pDigest)
  461. {
  462. return STATUS_INVALID_PARAMETER;
  463. }
  464. // Free up any allocs on a retry for directive decoding
  465. UnicodeStringFree(&pDigest->ustrUsername);
  466. UnicodeStringFree(&pDigest->ustrRealm);
  467. // Decode the username and realm directives
  468. if (pDigest->typeCharset == UTF_8)
  469. {
  470. DebugLog((DEB_TRACE, "DigestDecodeDirectiveStrings: UTF-8 Character set decoding\n"));
  471. Status = DecodeUnicodeString(&(pDigest->refstrParam[MD5_AUTH_USERNAME]),
  472. CP_UTF8,
  473. &pDigest->ustrUsername);
  474. if (!NT_SUCCESS (Status))
  475. {
  476. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: DecodeUnicodeString error 0x%x\n", Status));
  477. goto CleanUp;
  478. }
  479. Status = DecodeUnicodeString(&(pDigest->refstrParam[MD5_AUTH_REALM]),
  480. CP_UTF8,
  481. &pDigest->ustrRealm);
  482. if (!NT_SUCCESS (Status))
  483. {
  484. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: DecodeUnicodeString error 0x%x\n", Status));
  485. goto CleanUp;
  486. }
  487. }
  488. else if (pDigest->typeCharset == ISO_8859_1)
  489. {
  490. DebugLog((DEB_TRACE, "DigestDecodeDirectiveStrings: ISO-8859-1 Character set decoding\n"));
  491. Status = DecodeUnicodeString(&(pDigest->refstrParam[MD5_AUTH_USERNAME]),
  492. CP_8859_1,
  493. &pDigest->ustrUsername);
  494. if (!NT_SUCCESS (Status))
  495. {
  496. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: DecodeUnicodeString Username error 0x%x\n", Status));
  497. goto CleanUp;
  498. }
  499. Status = DecodeUnicodeString(&(pDigest->refstrParam[MD5_AUTH_REALM]),
  500. CP_8859_1,
  501. &pDigest->ustrRealm);
  502. if (!NT_SUCCESS (Status))
  503. {
  504. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: DecodeUnicodeString Realm error 0x%x\n", Status));
  505. goto CleanUp;
  506. }
  507. }
  508. else
  509. {
  510. Status = STATUS_UNMAPPABLE_CHARACTER;
  511. DebugLog((DEB_ERROR, "DigestDecodeDirectiveStrings: Unknown CharacterSet encoding for Digest parameters\n"));
  512. goto CleanUp;
  513. }
  514. DebugLog((DEB_TRACE, "DigestDecodeDirectiveStrings: Processing username (%wZ) realm (%wZ)\n",
  515. &pDigest->ustrUsername,
  516. &pDigest->ustrRealm));
  517. CleanUp:
  518. DebugLog((DEB_TRACE_FUNC, "DigestDecodeStrings Leaving\n"));
  519. return(Status);
  520. }
  521. //+--------------------------------------------------------------------
  522. //
  523. // Function: DigestCalcHA1
  524. //
  525. // Synopsis: Determine H(A1) for Digest Access
  526. //
  527. // Effects: Will calculate the SessionKey and store it in pDigest
  528. //
  529. // Arguments: pDigest - pointer to digest access data fields
  530. //
  531. // Returns: STATUS_SUCCESS for normal completion
  532. //
  533. // Notes: Called from DigestCalChalRsp
  534. // Sessionkey is H(A1)
  535. // Username and realm will be taken from the UserCreds
  536. //
  537. //---------------------------------------------------------------------
  538. NTSTATUS NTAPI
  539. DigestCalcHA1(IN PDIGEST_PARAMETER pDigest,
  540. IN PUSER_CREDENTIALS pUserCreds)
  541. {
  542. NTSTATUS Status = STATUS_SUCCESS;
  543. UNICODE_STRING ustrTempPasswd = {0};
  544. STRING strHPwKey = {0};
  545. STRING strBinaryHPwKey = {0};
  546. STRING strHA0Base = {0};
  547. STRING strHA0 = {0};
  548. STRING strPasswd = {0};
  549. STRING strUsername = {0};
  550. STRING strRealm = {0};
  551. PSTRING pstrAuthzID = NULL;
  552. BOOL fDefChars = FALSE;
  553. USHORT usHashOffset = 0;
  554. BOOL fSASLMode = FALSE;
  555. BOOL fValidHash = FALSE;
  556. int i = 0;
  557. DebugLog((DEB_TRACE_FUNC, "DigestCalcHA1: Entering\n"));
  558. ASSERT(pDigest);
  559. ASSERT(pUserCreds);
  560. if (!pUserCreds)
  561. {
  562. // No username & domain passed in
  563. Status = STATUS_INVALID_PARAMETER;
  564. DebugLog((DEB_ERROR, "DigestCalcHA1: No username info passed in\n"));
  565. goto CleanUp;
  566. }
  567. if ((pDigest->typeDigest == SASL_SERVER) || (pDigest->typeDigest == SASL_CLIENT))
  568. {
  569. fSASLMode = TRUE;
  570. }
  571. // Initialize local variables
  572. Status = StringAllocate(&strBinaryHPwKey, MD5_HASH_BYTESIZE);
  573. if (!NT_SUCCESS(Status))
  574. {
  575. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  576. Status = SEC_E_INSUFFICIENT_MEMORY;
  577. goto CleanUp;
  578. }
  579. Status = StringAllocate(&strHA0Base, MD5_HASH_HEX_SIZE);
  580. if (!NT_SUCCESS(Status))
  581. {
  582. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  583. Status = SEC_E_INSUFFICIENT_MEMORY;
  584. goto CleanUp;
  585. }
  586. Status = StringAllocate(&strHA0, MD5_HASH_HEX_SIZE);
  587. if (!NT_SUCCESS(Status))
  588. {
  589. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  590. Status = SEC_E_INSUFFICIENT_MEMORY;
  591. goto CleanUp;
  592. }
  593. // Check outputs
  594. if (pDigest->strSessionKey.MaximumLength <= MD5_HASH_HEX_SIZE)
  595. {
  596. StringFree(&(pDigest->strSessionKey));
  597. Status = StringAllocate(&(pDigest->strSessionKey), MD5_HASH_HEX_SIZE);
  598. if (!NT_SUCCESS(Status))
  599. {
  600. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  601. Status = SEC_E_INSUFFICIENT_MEMORY;
  602. goto CleanUp;
  603. }
  604. }
  605. if ((pUserCreds->fIsValidDigestHash == TRUE) && (pUserCreds->wHashSelected > 0))
  606. {
  607. // selected pre-calculated hash - retrieve from userCreds
  608. // read in precalc version number
  609. if (pUserCreds->strDigestHash.Length < MD5_HASH_BYTESIZE)
  610. {
  611. DebugLog((DEB_ERROR, "DigestCalcHA1: No Header on Precalc hash\n"));
  612. Status = SEC_E_NO_CREDENTIALS;
  613. goto CleanUp;
  614. }
  615. // check if valid hash number
  616. usHashOffset = pUserCreds->wHashSelected * MD5_HASH_BYTESIZE;
  617. if (pUserCreds->strDigestHash.Length < (usHashOffset + MD5_HASH_BYTESIZE))
  618. {
  619. DebugLog((DEB_ERROR, "DigestCalcHA1: Pre-calc hash not found\n"));
  620. Status = SEC_E_NO_CREDENTIALS;
  621. goto CleanUp;
  622. }
  623. // extract pre-calc hash - this is the binary version of the hash
  624. memcpy(strBinaryHPwKey.Buffer,
  625. (pUserCreds->strDigestHash.Buffer + usHashOffset),
  626. MD5_HASH_BYTESIZE);
  627. strBinaryHPwKey.Length = MD5_HASH_BYTESIZE;
  628. // all zero for hash indicates invalid hash calculated
  629. for (i=0; i < (int)strBinaryHPwKey.Length; i++)
  630. {
  631. if (strBinaryHPwKey.Buffer[i])
  632. {
  633. fValidHash = TRUE;
  634. break;
  635. }
  636. }
  637. if (fValidHash == FALSE)
  638. {
  639. // This is not a defined hash
  640. DebugLog((DEB_ERROR, "DigestCalcHA1: Invalid hash selected - not defined\n"));
  641. Status = SEC_E_NO_CREDENTIALS;
  642. goto CleanUp;
  643. }
  644. if (fSASLMode == TRUE)
  645. {
  646. // SASL mode keeps the Password hash in binary form
  647. Status = StringDuplicate(&strHPwKey, &strBinaryHPwKey);
  648. if (!NT_SUCCESS(Status))
  649. {
  650. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  651. Status = SEC_E_INSUFFICIENT_MEMORY;
  652. goto CleanUp;
  653. }
  654. }
  655. else
  656. {
  657. Status = StringAllocate(&strHPwKey, MD5_HASH_HEX_SIZE);
  658. if (!NT_SUCCESS(Status))
  659. {
  660. DebugLog((DEB_ERROR, "DigestCalcHA1: No Memory\n"));
  661. Status = SEC_E_INSUFFICIENT_MEMORY;
  662. goto CleanUp;
  663. }
  664. // HTTP mode needs to have HEX() version of password hash - RFC text is correct
  665. BinToHex((LPBYTE)strBinaryHPwKey.Buffer, MD5_HASH_BYTESIZE, strHPwKey.Buffer);
  666. strHPwKey.Length = MD5_HASH_HEX_SIZE; // Do not count the NULL at the end
  667. }
  668. #if DBG2
  669. if (fSASLMode == TRUE)
  670. {
  671. STRING strTempPwKey = {0};
  672. MyPrintBytes(strHPwKey.Buffer, strHPwKey.Length, &strTempPwKey);
  673. DebugLog((DEB_TRACE, "DigestCalcHA1: SASL Pre-Calc H(%wZ:%wZ:************) is %Z\n",
  674. &pUserCreds->ustrUsername, &pUserCreds->ustrRealm, &strTempPwKey));
  675. StringFree(&strTempPwKey);
  676. }
  677. else
  678. {
  679. DebugLog((DEB_TRACE, "DigestCalcHA1: HTTP Pre-Calc H(%wZ:%wZ:************) is %Z\n",
  680. &pUserCreds->ustrUsername, &pUserCreds->ustrRealm, &strHPwKey));
  681. }
  682. #endif
  683. }
  684. else if (pUserCreds->fIsValidPasswd == TRUE)
  685. {
  686. // copy over the passwd and decrypt if necessary
  687. Status = UnicodeStringDuplicatePassword(&ustrTempPasswd, &(pUserCreds->ustrPasswd));
  688. if (!NT_SUCCESS(Status))
  689. {
  690. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in dup password, status 0x%0x\n", Status ));
  691. goto CleanUp;
  692. }
  693. if ((pUserCreds->fIsEncryptedPasswd == TRUE) && (ustrTempPasswd.MaximumLength != 0))
  694. {
  695. g_LsaFunctions->LsaUnprotectMemory(ustrTempPasswd.Buffer, (ULONG)(ustrTempPasswd.MaximumLength));
  696. }
  697. // Need to encode the password for hash calculations
  698. // We have the cleartext password in ustrTempPasswd,
  699. // username in pContext->ustrAccountname,
  700. // realm in pContext->ustrDomain
  701. // Could do some code size optimization here in the future to shorten this up
  702. if (pDigest->typeCharset == UTF_8)
  703. {
  704. // First check if OK to encode in ISO 8859-1, if not then use UTF-8
  705. // All characters must be within ISO 8859-1 Character set else fail
  706. fDefChars = FALSE;
  707. Status = EncodeUnicodeString(&pUserCreds->ustrUsername, CP_8859_1, &strUsername, &fDefChars);
  708. if (!NT_SUCCESS(Status))
  709. {
  710. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding username\n"));
  711. Status = SEC_E_INSUFFICIENT_MEMORY;
  712. goto CleanUp;
  713. }
  714. if (fDefChars == TRUE)
  715. {
  716. DebugLog((DEB_TRACE, "DigestCalcHA1: Can not encode Username in 8859-1, use UTF-8\n"));
  717. StringFree(&strUsername);
  718. Status = EncodeUnicodeString(&pUserCreds->ustrUsername, CP_UTF8, &strUsername, NULL);
  719. if (!NT_SUCCESS(Status))
  720. {
  721. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding username\n"));
  722. Status = SEC_E_INSUFFICIENT_MEMORY;
  723. goto CleanUp;
  724. }
  725. }
  726. fDefChars = FALSE;
  727. Status = EncodeUnicodeString(&pUserCreds->ustrRealm, CP_8859_1, &strRealm, &fDefChars);
  728. if (!NT_SUCCESS(Status))
  729. {
  730. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding realm\n"));
  731. Status = SEC_E_INSUFFICIENT_MEMORY;
  732. goto CleanUp;
  733. }
  734. if (fDefChars == TRUE)
  735. {
  736. DebugLog((DEB_TRACE, "DigestCalcHA1: Can not encode realm in 8859-1, use UTF-8\n"));
  737. StringFree(&strRealm);
  738. Status = EncodeUnicodeString(&pUserCreds->ustrRealm, CP_UTF8, &strRealm, NULL);
  739. if (!NT_SUCCESS(Status))
  740. {
  741. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding realm\n"));
  742. Status = SEC_E_INSUFFICIENT_MEMORY;
  743. goto CleanUp;
  744. }
  745. }
  746. fDefChars = FALSE;
  747. Status = EncodeUnicodeString(&ustrTempPasswd, CP_8859_1, &strPasswd, &fDefChars);
  748. if (!NT_SUCCESS(Status))
  749. {
  750. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding passwd\n"));
  751. Status = SEC_E_INSUFFICIENT_MEMORY;
  752. goto CleanUp;
  753. }
  754. if (fDefChars == TRUE)
  755. {
  756. DebugLog((DEB_TRACE, "DigestCalcHA1: Can not encode password in 8859-1, use UTF-8\n"));
  757. if (strPasswd.Buffer && strPasswd.MaximumLength)
  758. {
  759. SecureZeroMemory(strPasswd.Buffer, strPasswd.MaximumLength);
  760. }
  761. StringFree(&strPasswd);
  762. Status = EncodeUnicodeString(&ustrTempPasswd, CP_UTF8, &strPasswd, NULL);
  763. if (!NT_SUCCESS(Status))
  764. {
  765. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding passwd\n"));
  766. Status = SEC_E_INSUFFICIENT_MEMORY;
  767. goto CleanUp;
  768. }
  769. }
  770. }
  771. else
  772. {
  773. // All characters must be within ISO 8859-1 Character set else fail
  774. Status = EncodeUnicodeString(&pUserCreds->ustrUsername, CP_8859_1, &strUsername, &fDefChars);
  775. if (!NT_SUCCESS(Status))
  776. {
  777. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding username\n"));
  778. Status = SEC_E_INSUFFICIENT_MEMORY;
  779. goto CleanUp;
  780. }
  781. if (fDefChars == TRUE)
  782. {
  783. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding username in ISO 8859-1\n"));
  784. Status = STATUS_UNMAPPABLE_CHARACTER;
  785. goto CleanUp;
  786. }
  787. Status = EncodeUnicodeString(&pUserCreds->ustrRealm, CP_8859_1, &strRealm, &fDefChars);
  788. if (!NT_SUCCESS(Status))
  789. {
  790. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding realm\n"));
  791. Status = SEC_E_INSUFFICIENT_MEMORY;
  792. goto CleanUp;
  793. }
  794. if (fDefChars == TRUE)
  795. {
  796. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding realm in ISO 8859-1\n"));
  797. Status = STATUS_UNMAPPABLE_CHARACTER;
  798. goto CleanUp;
  799. }
  800. Status = EncodeUnicodeString(&ustrTempPasswd, CP_8859_1, &strPasswd, &fDefChars);
  801. if (!NT_SUCCESS(Status))
  802. {
  803. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding passwd\n"));
  804. Status = SEC_E_INSUFFICIENT_MEMORY;
  805. goto CleanUp;
  806. }
  807. if (fDefChars == TRUE)
  808. {
  809. DebugLog((DEB_ERROR, "DigestCalcHA1: Error in encoding passwd in ISO 8859-1\n"));
  810. Status = STATUS_UNMAPPABLE_CHARACTER;
  811. goto CleanUp;
  812. }
  813. DebugLog((DEB_TRACE, "DigestCalcHA1: Username, Realm, Password encoded in ISO 8859-1\n"));
  814. }
  815. if (!strUsername.Length)
  816. {
  817. DebugLog((DEB_ERROR, "DigestCalcHA1: Must have non-zero length username\n"));
  818. Status = STATUS_INVALID_PARAMETER;
  819. goto CleanUp;
  820. }
  821. // If specified a no realm (NULL buffer, zero length) then used the REALM provided in the Challenge
  822. // if Realm specified as NULL string (valid buffer, zero length), then use a "" NULL string realm
  823. if (!pUserCreds->ustrRealm.Buffer)
  824. {
  825. ASSERT(!pUserCreds->ustrRealm.Length); // never have NULL buffer and a length
  826. StringFree(&strRealm);
  827. Status = StringDuplicate(&strRealm, &(pDigest->refstrParam[MD5_AUTH_REALM]));
  828. if (!NT_SUCCESS(Status))
  829. {
  830. DebugLog((DEB_ERROR, "DigestCalcHA1: Can not duplicate Challenge realm\n"));
  831. goto CleanUp;
  832. }
  833. DebugLog((DEB_ERROR, "DigestCalcHA1: Realm defaulted to Challenge realm value\n"));
  834. }
  835. // Calculate H(A1) based on Algorithm type
  836. // Auth is not specified or "MD5"
  837. // Use H(A1Base) = H(username-value:realm-value:passwd)
  838. if (fSASLMode == TRUE)
  839. {
  840. Status = DigestHash7(&strUsername,
  841. &strRealm,
  842. &strPasswd,
  843. NULL, NULL, NULL, NULL,
  844. FALSE, &strHPwKey);
  845. }
  846. else
  847. {
  848. Status = DigestHash7(&strUsername,
  849. &strRealm,
  850. &strPasswd,
  851. NULL, NULL, NULL, NULL,
  852. TRUE, &strHPwKey);
  853. }
  854. if (!NT_SUCCESS(Status))
  855. {
  856. DebugLog((DEB_ERROR, "DigestCalcHA1:DigestCalcHA1 H(PwKey) failed : 0x%x\n", Status));
  857. goto CleanUp;
  858. }
  859. #if DBG2
  860. if (fSASLMode == TRUE)
  861. {
  862. STRING strTempPwKey = {0};
  863. MyPrintBytes(strHPwKey.Buffer, strHPwKey.Length, &strTempPwKey);
  864. DebugLog((DEB_TRACE, "DigestCalcHA1: SASL Password Calc H(%Z:%Z:************) is %Z ******\n",
  865. &strUsername, &strRealm, &strTempPwKey));
  866. StringFree(&strTempPwKey);
  867. }
  868. else
  869. {
  870. DebugLog((DEB_TRACE, "DigestCalcHA1: HTTP Password Calc H(%Z:%Z:************) is %Z ******\n",
  871. &strUsername, &strRealm, &strHPwKey));
  872. }
  873. #endif
  874. }
  875. else
  876. {
  877. Status = SEC_E_NO_CREDENTIALS;
  878. DebugLog((DEB_ERROR, "DigestCalcHA1: No Pre-calc hash or password\n"));
  879. goto CleanUp;
  880. }
  881. // Check if using SASL then need to add in the AuthzID
  882. if (fSASLMode == TRUE)
  883. {
  884. // Set to use AuthzID otherwise keep the NULL
  885. // set only if AuthzID contains data
  886. if (pDigest->usFlags & FLAG_AUTHZID_PROVIDED)
  887. {
  888. pstrAuthzID = &(pDigest->refstrParam[MD5_AUTH_AUTHZID]);
  889. }
  890. }
  891. DebugLog((DEB_TRACE, "DigestCalcHA1: Algorithm type %d\n", pDigest->typeAlgorithm));
  892. // Now check if using MD5-SESS. We need to form
  893. // H(A1) = H( H(PwKey) : nonce : cnonce [: authzID])
  894. // otherwise simply set H(A1) = H(PwKey)
  895. if (pDigest->typeAlgorithm == MD5_SESS)
  896. {
  897. DebugLog((DEB_TRACE, "DigestCalcHA1: First client-server auth\n"));
  898. Status = DigestHash7(&strHPwKey,
  899. &(pDigest->refstrParam[MD5_AUTH_NONCE]),
  900. &(pDigest->refstrParam[MD5_AUTH_CNONCE]),
  901. pstrAuthzID,
  902. NULL, NULL, NULL,
  903. TRUE, &(pDigest->strSessionKey));
  904. if (!NT_SUCCESS(Status))
  905. {
  906. DebugLog((DEB_ERROR, "DigestCalcHA1: SessionKey failed : 0x%x\n", Status));
  907. goto CleanUp;
  908. }
  909. }
  910. else
  911. { // Keep SessionKey = H(PwKey) for Algoithm = MD5
  912. memcpy(pDigest->strSessionKey.Buffer, strHPwKey.Buffer, MD5_HASH_HEX_SIZE);
  913. pDigest->strSessionKey.Length = MD5_HASH_HEX_SIZE; // Do not count the NULL terminator
  914. }
  915. DebugLog((DEB_TRACE, "DigestCalcHA1: SessionKey is %.10Z**********\n", &(pDigest->strSessionKey)));
  916. Status = STATUS_SUCCESS;
  917. CleanUp:
  918. if (strBinaryHPwKey.Buffer && strBinaryHPwKey.MaximumLength)
  919. {
  920. SecureZeroMemory(strBinaryHPwKey.Buffer, strBinaryHPwKey.MaximumLength);
  921. }
  922. StringFree(&strBinaryHPwKey);
  923. if (strHPwKey.Buffer && strHPwKey.MaximumLength)
  924. {
  925. SecureZeroMemory(strHPwKey.Buffer, strHPwKey.MaximumLength);
  926. }
  927. StringFree(&strHPwKey);
  928. StringFree(&strHA0Base);
  929. StringFree(&strHA0);
  930. if (strPasswd.Buffer && strPasswd.MaximumLength)
  931. {
  932. SecureZeroMemory(strPasswd.Buffer, strPasswd.MaximumLength);
  933. }
  934. StringFree(&strPasswd);
  935. StringFree(&strUsername);
  936. StringFree(&strRealm);
  937. if (ustrTempPasswd.Buffer && ustrTempPasswd.MaximumLength)
  938. { // Zero out password info just to be safe
  939. SecureZeroMemory(ustrTempPasswd.Buffer, ustrTempPasswd.MaximumLength);
  940. }
  941. UnicodeStringFree(&ustrTempPasswd);
  942. DebugLog((DEB_TRACE_FUNC, "DigestCalcHA1: Leaving\n"));
  943. return(Status);
  944. }
  945. //+--------------------------------------------------------------------
  946. //
  947. // Function: DigestHash7
  948. //
  949. // Synopsis: Hash and Encode 7 STRINGS SOut = Hex(H(S1 S2 S3 S4 S5 S6 S7))
  950. //
  951. // Effects:
  952. //
  953. // Arguments: pS1,...,pS6 - STRINGS to hash, pS1 must be specified
  954. // fHexOut - perform a Hex operation on output
  955. // pSOut - STRING to hold Hex Encoded Hash
  956. //
  957. // Returns: STATUS_SUCCESS for normal completion
  958. //
  959. // Notes: pSOut->MaximumLength must be atleast (MD5_HASH_BYTESIZE (or MD5_HASH_HEX_SIZE) + sizeof(NULL))
  960. // Any pS# args which are NULL are skipped
  961. // if pS# is not NULL
  962. // Previously checked that pS# is non-zero length strings
  963. // You most likely want Sx->Length = strlen(Sx) so as not to include NULL
  964. // This function combines operations like H(S1 S2 S3), H(S1 S2 S3 S4 S5) ....
  965. // It is assumed that the char ':' is to be included getween Sn and Sn+1
  966. //
  967. //
  968. //---------------------------------------------------------------------
  969. NTSTATUS NTAPI
  970. DigestHash7(
  971. IN PSTRING pS1,
  972. IN PSTRING pS2,
  973. IN PSTRING pS3,
  974. IN PSTRING pS4,
  975. IN PSTRING pS5,
  976. IN PSTRING pS6,
  977. IN PSTRING pS7,
  978. IN BOOL fHexOut,
  979. OUT PSTRING pSOut)
  980. {
  981. NTSTATUS Status = STATUS_SUCCESS;
  982. HCRYPTHASH hHash = NULL;
  983. BYTE bHashData[MD5_HASH_BYTESIZE];
  984. DWORD cbHashData = MD5_HASH_BYTESIZE;
  985. USHORT usSizeRequired = 0;
  986. char *pbSeparator = COLONSTR;
  987. DebugLog((DEB_TRACE_FUNC, "DigestHash7: Entering \n"));
  988. ASSERT(pSOut);
  989. if (fHexOut == TRUE)
  990. {
  991. usSizeRequired = MD5_HASH_HEX_SIZE;
  992. }
  993. else
  994. {
  995. usSizeRequired = MD5_HASH_BYTESIZE;
  996. }
  997. // Check if output is proper size or allocate one
  998. if (!pSOut->Buffer)
  999. {
  1000. Status = StringAllocate(pSOut, usSizeRequired);
  1001. if (!NT_SUCCESS(Status))
  1002. {
  1003. DebugLog((DEB_ERROR, "DigestHash7: No Memory\n"));
  1004. Status = SEC_E_INSUFFICIENT_MEMORY;
  1005. goto CleanUp;
  1006. }
  1007. }
  1008. else
  1009. {
  1010. if (pSOut->MaximumLength < (usSizeRequired + 1))
  1011. {
  1012. // Output is not large enough to hold Hex(Hash)
  1013. DebugLog((DEB_ERROR, "DigestHash7: Output buffer too small\n"));
  1014. Status = STATUS_BUFFER_TOO_SMALL;
  1015. goto CleanUp;
  1016. }
  1017. }
  1018. if ( !CryptCreateHash( g_hCryptProv,
  1019. CALG_MD5,
  1020. 0,
  1021. 0,
  1022. &hHash ) )
  1023. {
  1024. DebugLog((DEB_ERROR, "DigestHash7: CryptCreateHash failed : 0x%lx\n", GetLastError()));
  1025. Status = STATUS_ENCRYPTION_FAILED;
  1026. goto CleanUp;
  1027. }
  1028. if (pS1)
  1029. {
  1030. if ( !CryptHashData( hHash,
  1031. (const unsigned char *)pS1->Buffer,
  1032. pS1->Length,
  1033. 0 ) )
  1034. {
  1035. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1036. CryptDestroyHash( hHash );
  1037. hHash = NULL;
  1038. Status = STATUS_ENCRYPTION_FAILED;
  1039. goto CleanUp;
  1040. }
  1041. }
  1042. if (pS2)
  1043. {
  1044. if ( !CryptHashData( hHash,
  1045. (const unsigned char *)pbSeparator,
  1046. COLONSTR_LEN,
  1047. 0 ) )
  1048. {
  1049. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1050. CryptDestroyHash( hHash );
  1051. hHash = NULL;
  1052. Status = STATUS_ENCRYPTION_FAILED;
  1053. goto CleanUp;
  1054. }
  1055. if ( !CryptHashData( hHash,
  1056. (const unsigned char *)pS2->Buffer,
  1057. pS2->Length,
  1058. 0 ) )
  1059. {
  1060. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1061. CryptDestroyHash( hHash );
  1062. hHash = NULL;
  1063. Status = STATUS_ENCRYPTION_FAILED;
  1064. goto CleanUp;
  1065. }
  1066. }
  1067. if (pS3)
  1068. {
  1069. (void)CryptHashData( hHash,
  1070. (const unsigned char *)pbSeparator,
  1071. COLONSTR_LEN,
  1072. 0 );
  1073. if ( !CryptHashData( hHash,
  1074. (const unsigned char *)pS3->Buffer,
  1075. pS3->Length,
  1076. 0 ) )
  1077. {
  1078. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1079. CryptDestroyHash( hHash );
  1080. hHash = NULL;
  1081. Status = STATUS_ENCRYPTION_FAILED;
  1082. goto CleanUp;
  1083. }
  1084. }
  1085. if (pS4)
  1086. {
  1087. (void)CryptHashData( hHash,
  1088. (const unsigned char *)pbSeparator,
  1089. COLONSTR_LEN,
  1090. 0 );
  1091. if ( !CryptHashData( hHash,
  1092. (const unsigned char *)pS4->Buffer,
  1093. pS4->Length,
  1094. 0 ) )
  1095. {
  1096. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1097. CryptDestroyHash( hHash );
  1098. hHash = NULL;
  1099. Status = STATUS_ENCRYPTION_FAILED;
  1100. goto CleanUp;
  1101. }
  1102. }
  1103. if (pS5)
  1104. {
  1105. (void)CryptHashData( hHash,
  1106. (const unsigned char *)pbSeparator,
  1107. COLONSTR_LEN,
  1108. 0 );
  1109. if ( !CryptHashData( hHash,
  1110. (const unsigned char *)pS5->Buffer,
  1111. pS5->Length,
  1112. 0 ) )
  1113. {
  1114. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1115. CryptDestroyHash( hHash );
  1116. hHash = NULL;
  1117. Status = STATUS_ENCRYPTION_FAILED;
  1118. goto CleanUp;
  1119. }
  1120. }
  1121. if (pS6)
  1122. {
  1123. (void)CryptHashData( hHash,
  1124. (const unsigned char *)pbSeparator,
  1125. COLONSTR_LEN,
  1126. 0 );
  1127. if ( !CryptHashData( hHash,
  1128. (const unsigned char *)pS6->Buffer,
  1129. pS6->Length,
  1130. 0 ) )
  1131. {
  1132. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1133. CryptDestroyHash( hHash );
  1134. hHash = NULL;
  1135. Status = STATUS_ENCRYPTION_FAILED;
  1136. goto CleanUp;
  1137. }
  1138. }
  1139. if (pS7)
  1140. {
  1141. (void)CryptHashData( hHash,
  1142. (const unsigned char *)pbSeparator,
  1143. COLONSTR_LEN,
  1144. 0 );
  1145. if ( !CryptHashData( hHash,
  1146. (const unsigned char *)pS7->Buffer,
  1147. pS7->Length,
  1148. 0 ) )
  1149. {
  1150. DebugLog((DEB_ERROR, "DigestHash7: CryptHashData failed : 0x%lx\n", GetLastError()));
  1151. CryptDestroyHash( hHash );
  1152. hHash = NULL;
  1153. Status = STATUS_ENCRYPTION_FAILED;
  1154. goto CleanUp;
  1155. }
  1156. }
  1157. if ( !CryptGetHashParam( hHash,
  1158. HP_HASHVAL,
  1159. bHashData,
  1160. &cbHashData,
  1161. 0 ) )
  1162. {
  1163. DebugLog((DEB_ERROR, "DigestHash7: CryptGetHashParam failed : 0x%lx\n", GetLastError()));
  1164. CryptDestroyHash( hHash );
  1165. hHash = NULL;
  1166. Status = STATUS_ENCRYPTION_FAILED;
  1167. goto CleanUp;
  1168. }
  1169. CryptDestroyHash( hHash );
  1170. hHash = NULL;
  1171. ASSERT(cbHashData == MD5_HASH_BYTESIZE);
  1172. if (fHexOut == TRUE)
  1173. {
  1174. BinToHex(bHashData, cbHashData, pSOut->Buffer);
  1175. pSOut->Length = MD5_HASH_HEX_SIZE; // Do not count the NULL at the end
  1176. }
  1177. else
  1178. {
  1179. memcpy(pSOut->Buffer, bHashData, cbHashData);
  1180. pSOut->Length = MD5_HASH_BYTESIZE; // Do not count the NULL at the end
  1181. }
  1182. CleanUp:
  1183. DebugLog((DEB_TRACE_FUNC, "DigestHash7: Leaving Status 0x%x\n", Status));
  1184. return(Status);
  1185. }
  1186. // Blob creation/extraction for GenericPassthrough Messages
  1187. //+--------------------------------------------------------------------
  1188. //
  1189. // Function: BlobEncodeRequest
  1190. //
  1191. // Synopsis: Encode the Digest Access Parameters fields into a BYTE Buffer
  1192. //
  1193. // Effects: Creates a Buffer allocation which calling function
  1194. // is responsible to delete with call to BlobFreeRequest()
  1195. //
  1196. // Arguments: pDigest - pointer to digest access data fields
  1197. //
  1198. // Returns:
  1199. //
  1200. // Notes: STATUS_SUCCESS for normal completion
  1201. //
  1202. //
  1203. //---------------------------------------------------------------------
  1204. NTSTATUS NTAPI
  1205. BlobEncodeRequest(
  1206. PDIGEST_PARAMETER pDigest,
  1207. OUT BYTE **ppOutBuffer,
  1208. OUT USHORT *cbOutBuffer
  1209. )
  1210. {
  1211. NTSTATUS Status = STATUS_SUCCESS;
  1212. DebugLog((DEB_TRACE_FUNC, "BlobEncodeRequest: Entering\n"));
  1213. USHORT cbBuffer = 0;
  1214. BYTE *pBuffer = NULL;
  1215. char *pch = NULL;
  1216. PDIGEST_BLOB_REQUEST pHeader;
  1217. int i = 0;
  1218. USHORT cbValue = 0;
  1219. USHORT cbAccountName = 0;
  1220. USHORT cbCrackedDomain = 0;
  1221. USHORT cbWorkstation = 0;
  1222. // Now figure out how many bytes needed to hold field-value NULL terminated
  1223. for (i=0, cbBuffer = 0;i < DIGEST_BLOB_VALUES;i++)
  1224. {
  1225. if (pDigest->refstrParam[i].Buffer && pDigest->refstrParam[i].Length)
  1226. { // may be able to just count str.length
  1227. cbBuffer = cbBuffer + strlencounted(pDigest->refstrParam[i].Buffer, pDigest->refstrParam[i].MaximumLength);
  1228. }
  1229. }
  1230. cbBuffer = cbBuffer + (DIGEST_BLOB_VALUES * sizeof(char)); // Account for the separating/terminating NULLs
  1231. // Now add in space for the DSCrackName accountname and domain
  1232. if (pDigest->ustrCrackedAccountName.Buffer && pDigest->ustrCrackedAccountName.Length)
  1233. {
  1234. cbAccountName = ustrlencounted((const short *)pDigest->ustrCrackedAccountName.Buffer,
  1235. pDigest->ustrCrackedAccountName.Length) * sizeof(WCHAR);
  1236. cbBuffer = cbBuffer + cbAccountName;
  1237. }
  1238. if (pDigest->ustrCrackedDomain.Buffer && pDigest->ustrCrackedDomain.Length)
  1239. {
  1240. cbCrackedDomain = ustrlencounted((const short *)pDigest->ustrCrackedDomain.Buffer,
  1241. pDigest->ustrCrackedDomain.Length) * sizeof(WCHAR);
  1242. cbBuffer = cbBuffer + cbCrackedDomain;
  1243. }
  1244. cbBuffer = cbBuffer + (2 * sizeof(WCHAR)); // Account for the separating/terminating NULLs
  1245. // Now add in space for the workstation/server name
  1246. if (g_ustrWorkstationName.Buffer && g_ustrWorkstationName.Length)
  1247. {
  1248. cbWorkstation = ustrlencounted((const short *)g_ustrWorkstationName.Buffer,
  1249. g_ustrWorkstationName.Length) * sizeof(WCHAR);
  1250. cbBuffer = cbBuffer + cbWorkstation;
  1251. }
  1252. cbBuffer = cbBuffer + sizeof(WCHAR); // Account for the separating/terminating NULL
  1253. cbValue = cbBuffer + (sizeof(DIGEST_BLOB_REQUEST));
  1254. pBuffer = (BYTE *)DigestAllocateMemory(cbValue);
  1255. if (!pBuffer)
  1256. {
  1257. DebugLog((DEB_ERROR, "BlobEncodeRequest out of memory\n"));
  1258. Status = SEC_E_INSUFFICIENT_MEMORY;
  1259. goto CleanUp;
  1260. }
  1261. DebugLog((DEB_TRACE, "BlobEncodeRequest using %d bytes\n", cbValue));
  1262. *cbOutBuffer = cbValue; // Return number of bytes we are using for the encoding
  1263. // Now fill in the information
  1264. pHeader = (PDIGEST_BLOB_REQUEST)pBuffer;
  1265. pHeader->MessageType = VERIFY_DIGEST_MESSAGE;
  1266. pHeader->version = DIGEST_BLOB_VERSION;
  1267. pHeader->digest_type = (USHORT)pDigest->typeDigest;
  1268. pHeader->qop_type = (USHORT)pDigest->typeQOP;
  1269. pHeader->alg_type = (USHORT)pDigest->typeAlgorithm;
  1270. pHeader->charset_type = (USHORT)pDigest->typeCharset;
  1271. pHeader->name_format = (USHORT)pDigest->typeName; // Format of the username
  1272. pHeader->cbCharValues = cbBuffer;
  1273. pHeader->cbBlobSize = cbValue; // cbCharValues + charvalues
  1274. pHeader->cbAccountName = cbAccountName + sizeof(WCHAR); // string length includes NULL terminator
  1275. pHeader->cbCrackedDomain = cbCrackedDomain + sizeof(WCHAR);
  1276. pHeader->cbWorkstation = cbWorkstation + sizeof(WCHAR);
  1277. pHeader->usFlags = pDigest->usFlags;
  1278. // Simply copy over the first DIGEST_BLOB_VALUES that are arranged to be
  1279. for (i = 0,pch = &(pHeader->cCharValues); i < DIGEST_BLOB_VALUES;i++)
  1280. {
  1281. // Make sure that there is valid data to get length from
  1282. if (pDigest->refstrParam[i].Buffer && pDigest->refstrParam[i].Length)
  1283. {
  1284. cbValue = strlencounted(pDigest->refstrParam[i].Buffer, pDigest->refstrParam[i].MaximumLength);
  1285. // dont use .length since may include multiple NULLS
  1286. memcpy(pch, pDigest->refstrParam[i].Buffer, cbValue);
  1287. }
  1288. else
  1289. cbValue = 0;
  1290. pch += (cbValue + 1); // This will leave one NULL at end of field-value
  1291. }
  1292. // Now write out any results from DSCrackName
  1293. if (pDigest->ustrCrackedAccountName.Buffer && pDigest->ustrCrackedAccountName.Length)
  1294. {
  1295. memcpy(pch, pDigest->ustrCrackedAccountName.Buffer, cbAccountName);
  1296. }
  1297. pch += (cbAccountName + sizeof(WCHAR)); // This will leave one WCHAR NULL at end of CrackedAccountName
  1298. if (pDigest->ustrCrackedDomain.Buffer && pDigest->ustrCrackedDomain.Length)
  1299. {
  1300. memcpy(pch, pDigest->ustrCrackedDomain.Buffer, cbCrackedDomain);
  1301. }
  1302. pch += (cbCrackedDomain + sizeof(WCHAR)); // This will leave one WCHAR NULL at end of CrackedAccountName
  1303. if (g_ustrWorkstationName.Buffer && g_ustrWorkstationName.Length)
  1304. {
  1305. memcpy(pch, g_ustrWorkstationName.Buffer, cbWorkstation);
  1306. }
  1307. else
  1308. pch += (cbWorkstation + sizeof(WCHAR)); // This will leave one WCHAR NULL at end of Workstation name
  1309. *ppOutBuffer = pBuffer; // Pass off memory back to calling routine
  1310. DebugLog((DEB_TRACE, "BlobEncodeRequest: message_type 0x%x, version %d, CharValues %d, BlobSize %d\n",
  1311. pHeader->digest_type, pHeader->version, pHeader->cbCharValues, pHeader->cbBlobSize));
  1312. CleanUp:
  1313. DebugLog((DEB_TRACE_FUNC, "BlobEncodeRequest: Leaving Status 0x%x\n", Status));
  1314. return(Status);
  1315. }
  1316. //+--------------------------------------------------------------------
  1317. //
  1318. // Function: BlobDecodeRequest
  1319. //
  1320. // Synopsis: Decode the Digest Access Parameters fields from a BYTE Buffer
  1321. //
  1322. // Arguments: pBuffer - pointer to BlobEncodeRequestd buffer as input
  1323. // pDigest - pointer to Digest parameter struct to set STRINGS
  1324. // to point within pBuffer. No string memory is allocated
  1325. //
  1326. // Returns: NTSTATUS
  1327. //
  1328. // Notes:
  1329. // Currently only processes a single version of the packet. Check MessageType
  1330. // and version number if new message types are supported on the DC.
  1331. //
  1332. //---------------------------------------------------------------------
  1333. NTSTATUS NTAPI BlobDecodeRequest(
  1334. IN USHORT cbBuffer,
  1335. IN BYTE *pBuffer,
  1336. PDIGEST_PARAMETER pDigest
  1337. )
  1338. {
  1339. NTSTATUS Status = STATUS_SUCCESS;
  1340. DIGEST_BLOB_REQUEST Header;
  1341. PDIGEST_BLOB_REQUEST pHeader;
  1342. char *pch = NULL;
  1343. USHORT sLen = 0;
  1344. int i = 0; // counter
  1345. BOOL fKnownFormat = FALSE;
  1346. USHORT sMaxRead = 0;
  1347. PWCHAR pusTemp = NULL;
  1348. PWCHAR pusLoc = NULL;
  1349. USHORT usCnt = 0;
  1350. //UNREFERENCED_PARAMETER(cbBuffer);
  1351. DebugLog((DEB_TRACE_FUNC, "BlobDecodeRequest: Entering\n"));
  1352. if (!pBuffer || !pDigest)
  1353. {
  1354. DebugLog((DEB_ERROR, "BlobDecodeRequest: Invalid parameter\n"));
  1355. Status = STATUS_INVALID_PARAMETER;
  1356. goto CleanUp;
  1357. }
  1358. // Copy over the header for byte alignment
  1359. if (cbBuffer < sizeof(Header))
  1360. {
  1361. DebugLog((DEB_ERROR, "BlobDecodeRequest: Header block not present\n"));
  1362. Status = STATUS_INVALID_PARAMETER;
  1363. goto CleanUp;
  1364. }
  1365. memcpy((char *)&Header, (char *)pBuffer, sizeof(Header));
  1366. DebugLog((DEB_TRACE, "BlobDecodeRequest: message_type %lu version %d, CharValues %d, BlobSize %d\n",
  1367. Header.MessageType, Header.version, Header.cbCharValues, Header.cbBlobSize));
  1368. // Process the encoded message - use only the known MessageTypes and versions here on the DC
  1369. // This allows for expansion of protocols supported in the future
  1370. if ((Header.MessageType == VERIFY_DIGEST_MESSAGE) && (Header.version == DIGEST_BLOB_VERSION))
  1371. {
  1372. fKnownFormat = TRUE;
  1373. DebugLog((DEB_TRACE, "BlobDecodeRequest: Blob from server known type and version\n"));
  1374. }
  1375. if (!fKnownFormat)
  1376. {
  1377. DebugLog((DEB_ERROR, "BlobDecodeRequest: Not supported MessageType/Version\n"));
  1378. Status = STATUS_INVALID_PARAMETER;
  1379. goto CleanUp;
  1380. }
  1381. pDigest->typeDigest = (DIGEST_TYPE)Header.digest_type;
  1382. pDigest->typeQOP = (QOP_TYPE)Header.qop_type;
  1383. pDigest->typeAlgorithm = (ALGORITHM_TYPE)Header.alg_type;
  1384. pDigest->typeCharset = (CHARSET_TYPE)Header.charset_type;
  1385. pDigest->typeName = (NAMEFORMAT_TYPE)Header.name_format;
  1386. pDigest->usFlags = (USHORT)Header.usFlags;
  1387. DebugLog((DEB_TRACE, "BlobDecodeRequest: typeDigest 0x%x, typeQOP %d, typeAlgorithm %d, typeCharset %d NameFormat %d\n",
  1388. pDigest->typeDigest, pDigest->typeQOP, pDigest->typeAlgorithm, pDigest->typeCharset, pDigest->typeName));
  1389. pHeader = (PDIGEST_BLOB_REQUEST)pBuffer;
  1390. pch = &(pHeader->cCharValues); // strings start on last char of struct
  1391. sMaxRead = Header.cbCharValues;
  1392. for (i = 0; i < DIGEST_BLOB_VALUES;i++)
  1393. {
  1394. sLen = strlencounted(pch, sMaxRead);
  1395. if (!sLen)
  1396. {
  1397. // Null String no value skip to next
  1398. pch++;
  1399. sMaxRead--;
  1400. }
  1401. else
  1402. { // Simple check to make sure that we do not copy way too much
  1403. if (sLen < (Header.cbCharValues))
  1404. {
  1405. DebugLog((DEB_TRACE, "BlobDecodeRequest: Setting Digest[%d] = %s\n", i, pch));
  1406. pDigest->refstrParam[i].Buffer = pch;
  1407. pDigest->refstrParam[i].Length = sLen;
  1408. pDigest->refstrParam[i].MaximumLength = sLen+1;
  1409. pch += (sLen + 1); // skip over field-value and NULL
  1410. sMaxRead -= (sLen + 1);
  1411. }
  1412. else
  1413. {
  1414. // This indicates failed NULL separators in BlobData
  1415. // Really should not happen unless encoded wrong
  1416. Status = STATUS_INTERNAL_DB_CORRUPTION;
  1417. memset(pDigest, 0, sizeof(DIGEST_PARAMETER)); // scrubbed all info
  1418. DebugLog((DEB_ERROR, "BlobDecodeRequest: NULL separator missing\n"));
  1419. goto CleanUp;
  1420. }
  1421. }
  1422. }
  1423. // Read in the values that DSCrackName on the server found out
  1424. // Need to place on SHORT boundary for Unicode string processing
  1425. usCnt = sMaxRead + (3 * sizeof(WCHAR));
  1426. pusTemp = (PWCHAR)DigestAllocateMemory(usCnt); // Force a NULL terminator just to be safe
  1427. if (!pusTemp)
  1428. {
  1429. Status = STATUS_NO_MEMORY;
  1430. DebugLog((DEB_ERROR, "BlobDecodeRequest: Memory Alloc Error\n"));
  1431. goto CleanUp;
  1432. }
  1433. // Format will be Unicode_account_name NULL Unicode_domain_name NULL Unicode_WorkstationName NULL [NULL NULL NULL]
  1434. memcpy((PCHAR)pusTemp, pch, sMaxRead);
  1435. // Read out the three unicode strings
  1436. Status = UnicodeStringWCharDuplicate(&(pDigest->ustrCrackedAccountName), pusTemp, 0);
  1437. if (!NT_SUCCESS(Status))
  1438. {
  1439. DebugLog((DEB_ERROR,"BlobDecodeRequest: Failed to duplicate Account Name: 0x%x\n",Status));
  1440. goto CleanUp;
  1441. }
  1442. pusLoc = pusTemp + (1 + (pDigest->ustrCrackedAccountName.Length / sizeof(WCHAR))); // Skip NULL
  1443. Status = UnicodeStringWCharDuplicate(&(pDigest->ustrCrackedDomain), pusLoc, 0);
  1444. if (!NT_SUCCESS(Status))
  1445. {
  1446. DebugLog((DEB_ERROR,"BlobDecodeRequest: Failed to duplicate Domain Name: 0x%x\n",Status));
  1447. goto CleanUp;
  1448. }
  1449. pusLoc = pusTemp + (2 + ((pDigest->ustrCrackedAccountName.Length + pDigest->ustrCrackedDomain.Length) / sizeof(WCHAR))); // Skip NULL
  1450. Status = UnicodeStringWCharDuplicate(&(pDigest->ustrWorkstation), pusLoc, 0);
  1451. if (!NT_SUCCESS(Status))
  1452. {
  1453. DebugLog((DEB_ERROR,"BlobDecodeRequest: Failed to duplicate Workstation Name: 0x%x\n",Status));
  1454. goto CleanUp;
  1455. }
  1456. DebugLog((DEB_TRACE,"BlobDecodeRequest: Cracked Account %wZ Domain %wZ Workstation %wZ\n",
  1457. &(pDigest->ustrCrackedAccountName),
  1458. &(pDigest->ustrCrackedDomain),
  1459. &(pDigest->ustrWorkstation)));
  1460. // If AuthzID directive was present but no length then create a valid buffer but zero length
  1461. if ((pDigest->usFlags & FLAG_AUTHZID_PROVIDED) &&
  1462. !pDigest->refstrParam[MD5_AUTH_AUTHZID].Buffer)
  1463. {
  1464. pDigest->refstrParam[MD5_AUTH_AUTHZID].Buffer = &(pHeader->cCharValues); // strings start on last char of struct;
  1465. pDigest->refstrParam[MD5_AUTH_AUTHZID].Length = 0;
  1466. pDigest->refstrParam[MD5_AUTH_AUTHZID].MaximumLength = 0;
  1467. }
  1468. CleanUp:
  1469. DebugLog((DEB_TRACE_FUNC, "BlobDecodeRequest: Leaving 0x%x\n", Status));
  1470. if (pusTemp)
  1471. {
  1472. DigestFreeMemory(pusTemp);
  1473. pusTemp = NULL;
  1474. }
  1475. return(Status);
  1476. }
  1477. // Free BYTE Buffer from BlobEncodeRequest
  1478. VOID NTAPI BlobFreeRequest(
  1479. BYTE *pBuffer
  1480. )
  1481. {
  1482. if (pBuffer)
  1483. {
  1484. DigestFreeMemory(pBuffer);
  1485. }
  1486. return;
  1487. }
  1488. #else // SECURITY_KERNEL
  1489. //+--------------------------------------------------------------------
  1490. //
  1491. // Function: DigestHash7 - kernel mode
  1492. //
  1493. // Synopsis: Hash and Encode 7 STRINGS SOut = Hex(H(S1 S2 S3 S4 S5 S6 S7))
  1494. //
  1495. // Effects:
  1496. //
  1497. // Arguments: pS1,...,pS6 - STRINGS to hash, pS1 must be specified
  1498. // fHexOut - perform a Hex operation on output
  1499. // pSOut - STRING to hold Hex Encoded Hash
  1500. //
  1501. // Returns: STATUS_SUCCESS for normal completion
  1502. //
  1503. // Notes: pSOut->MaximumLength must be atleast (MD5_HASH_BYTESIZE (or MD5_HASH_HEX_SIZE) + sizeof(NULL))
  1504. // Any pS# args which are NULL are skipped
  1505. // if pS# is not NULL
  1506. // Previously checked that pS# is non-zero length strings
  1507. // You most likely want Sx->Length = strlen(Sx) so as not to include NULL
  1508. // This function combines operations like H(S1 S2 S3), H(S1 S2 S3 S4 S5) ....
  1509. // It is assumed that the char ':' is to be included getween Sn and Sn+1
  1510. //
  1511. //
  1512. //---------------------------------------------------------------------
  1513. NTSTATUS NTAPI
  1514. DigestHash7(
  1515. IN PSTRING pS1,
  1516. IN PSTRING pS2,
  1517. IN PSTRING pS3,
  1518. IN PSTRING pS4,
  1519. IN PSTRING pS5,
  1520. IN PSTRING pS6,
  1521. IN PSTRING pS7,
  1522. IN BOOL fHexOut,
  1523. OUT PSTRING pSOut)
  1524. {
  1525. NTSTATUS Status = STATUS_SUCCESS;
  1526. MD5_CTX Md5Context;
  1527. USHORT usSizeRequired = 0;
  1528. char *pbSeparator = COLONSTR;
  1529. DebugLog((DEB_TRACE_FUNC, "DigestHash7: Entering \n"));
  1530. // Verify the size of the output digest is what we assume
  1531. ASSERT(MD5DIGESTLEN == MD5_HASH_BYTESIZE);
  1532. ASSERT(pSOut);
  1533. if (fHexOut == TRUE)
  1534. {
  1535. usSizeRequired = MD5_HASH_HEX_SIZE;
  1536. }
  1537. else
  1538. {
  1539. usSizeRequired = MD5_HASH_BYTESIZE;
  1540. }
  1541. // Check if output is proper size or allocate one
  1542. if (!pSOut->Buffer)
  1543. {
  1544. Status = StringAllocate(pSOut, usSizeRequired);
  1545. if (!NT_SUCCESS(Status))
  1546. {
  1547. DebugLog((DEB_ERROR, "DigestHash7: No Memory\n"));
  1548. Status = SEC_E_INSUFFICIENT_MEMORY;
  1549. goto CleanUp;
  1550. }
  1551. }
  1552. else
  1553. {
  1554. if (pSOut->MaximumLength < (usSizeRequired + 1))
  1555. {
  1556. // Output is not large enough to hold Hex(Hash)
  1557. DebugLog((DEB_ERROR, "DigestHash7: Output buffer too small\n"));
  1558. Status = STATUS_BUFFER_TOO_SMALL;
  1559. goto CleanUp;
  1560. }
  1561. }
  1562. MD5Init(&Md5Context);
  1563. if (pS1)
  1564. {
  1565. MD5Update(&Md5Context, (const unsigned char *)pS1->Buffer, pS1->Length);
  1566. }
  1567. if (pS2)
  1568. {
  1569. MD5Update(&Md5Context, (const unsigned char *)pbSeparator, COLONSTR_LEN);
  1570. MD5Update(&Md5Context, (const unsigned char *)pS2->Buffer, pS2->Length);
  1571. }
  1572. if (pS3)
  1573. {
  1574. MD5Update(&Md5Context, (const unsigned char *)pbSeparator, COLONSTR_LEN);
  1575. MD5Update(&Md5Context, (const unsigned char *)pS3->Buffer, pS3->Length);
  1576. }
  1577. if (pS4)
  1578. {
  1579. MD5Update(&Md5Context, (const unsigned char *)pbSeparator, COLONSTR_LEN);
  1580. MD5Update(&Md5Context, (const unsigned char *)pS4->Buffer, pS4->Length);
  1581. }
  1582. if (pS5)
  1583. {
  1584. MD5Update(&Md5Context, (const unsigned char *)pbSeparator, COLONSTR_LEN);
  1585. MD5Update(&Md5Context, (const unsigned char *)pS5->Buffer, pS5->Length);
  1586. }
  1587. if (pS6)
  1588. {
  1589. MD5Update(&Md5Context, (const unsigned char *)pbSeparator, COLONSTR_LEN);
  1590. MD5Update(&Md5Context, (const unsigned char *)pS6->Buffer, pS6->Length);
  1591. }
  1592. if (pS7)
  1593. {
  1594. MD5Update(&Md5Context, (const unsigned char *)pbSeparator, COLONSTR_LEN);
  1595. MD5Update(&Md5Context, (const unsigned char *)pS7->Buffer, pS7->Length);
  1596. }
  1597. MD5Final(&Md5Context);
  1598. if (fHexOut == TRUE)
  1599. {
  1600. BinToHex((LPBYTE)Md5Context.digest, MD5_HASH_BYTESIZE, pSOut->Buffer);
  1601. pSOut->Length = MD5_HASH_HEX_SIZE; // Do not count the NULL at the end
  1602. }
  1603. else
  1604. {
  1605. RtlCopyMemory(pSOut->Buffer, Md5Context.digest, MD5_HASH_BYTESIZE);
  1606. pSOut->Length = MD5_HASH_BYTESIZE; // Do not count the NULL at the end
  1607. }
  1608. CleanUp:
  1609. DebugLog((DEB_TRACE_FUNC, "DigestHash7: Leaving Status 0x%x\n", Status));
  1610. return(Status);
  1611. }
  1612. #endif // SECURITY_KERNEL
  1613. // Generate the output buffer from a given Digest
  1614. NTSTATUS NTAPI
  1615. DigestCreateChalResp(
  1616. IN PDIGEST_PARAMETER pDigest,
  1617. IN PUSER_CREDENTIALS pUserCreds,
  1618. OUT PSecBuffer OutBuffer
  1619. )
  1620. {
  1621. NTSTATUS Status = STATUS_SUCCESS;
  1622. ULONG cbLenNeeded = 0;
  1623. STRING strcQOP = {0}; // string pointing to a constant value
  1624. STRING strcAlgorithm = {0};
  1625. BOOL fSASLMode = FALSE;
  1626. UINT uCodePage = CP_8859_1;
  1627. STRING strTempRealm = {0}; // Backslash encoded forms
  1628. STRING strTempUsername = {0};
  1629. STRING strRealm = {0};
  1630. STRING strUsername = {0};
  1631. PSTRING pstrUsername = NULL;
  1632. PSTRING pstrRealm = NULL;
  1633. PCHAR pczTemp = NULL;
  1634. PCHAR pczTemp2 = NULL;
  1635. DebugLog((DEB_TRACE_FUNC, "DigestCreateChalResp: Entering\n"));
  1636. // allocate the buffers for output - in the future can optimze to allocate exact amount needed
  1637. pczTemp = (PCHAR)DigestAllocateMemory((3 * NTDIGEST_SP_MAX_TOKEN_SIZE) + 1);
  1638. if (!pczTemp)
  1639. {
  1640. DebugLog((DEB_ERROR, "DigestCreateChalResp: No memory for output buffers\n"));
  1641. goto CleanUp;
  1642. }
  1643. pczTemp2 = (PCHAR)DigestAllocateMemory(NTDIGEST_SP_MAX_TOKEN_SIZE + 1);
  1644. if (!pczTemp2)
  1645. {
  1646. DebugLog((DEB_ERROR, "DigestCreateChalResp: No memory for output buffers\n"));
  1647. goto CleanUp;
  1648. }
  1649. //pczTemp[0] = '\0'; DigestAllocateMemory() should have zeroed the bytes
  1650. //pczTemp2[0] = '\0';
  1651. if ((pDigest->typeDigest == SASL_SERVER) || (pDigest->typeDigest == SASL_CLIENT))
  1652. {
  1653. fSASLMode = TRUE;
  1654. }
  1655. // Establish which QOP utilized
  1656. if (pDigest->typeQOP == AUTH_CONF)
  1657. {
  1658. RtlInitString(&strcQOP, AUTHCONFSTR);
  1659. }
  1660. else if (pDigest->typeQOP == AUTH_INT)
  1661. {
  1662. RtlInitString(&strcQOP, AUTHINTSTR);
  1663. }
  1664. else if (pDigest->typeQOP == AUTH)
  1665. {
  1666. RtlInitString(&strcQOP, AUTHSTR);
  1667. }
  1668. // Determine which code page to utilize
  1669. if (pDigest->typeCharset == UTF_8)
  1670. {
  1671. uCodePage = CP_UTF8;
  1672. }
  1673. else
  1674. {
  1675. uCodePage = CP_8859_1;
  1676. }
  1677. // if provided with UserCred then use them, otherwise use Digest directive values
  1678. if (pUserCreds)
  1679. {
  1680. #ifndef SECURITY_KERNEL
  1681. DebugLog((DEB_TRACE, "DigestCreateChalResp: UserCredentials presented - encode and output\n"));
  1682. Status = EncodeUnicodeString(&pUserCreds->ustrUsername, uCodePage, &strUsername, NULL);
  1683. if (!NT_SUCCESS(Status))
  1684. {
  1685. DebugLog((DEB_WARN, "DigestCreateChalResp: Error in encoding username\n"));
  1686. goto CleanUp;
  1687. }
  1688. // Now encode the user directed fields (username, URI, realm)
  1689. Status = BackslashEncodeString(&strUsername, &strTempUsername);
  1690. if (!NT_SUCCESS (Status))
  1691. {
  1692. DebugLog((DEB_ERROR, "DigestCreateChalResp: BackslashEncode failed status 0x%x\n", Status));
  1693. goto CleanUp;
  1694. }
  1695. pstrUsername = &strTempUsername;
  1696. // Make copy of the directive values for LSA to Usermode context
  1697. Status = StringDuplicate(&(pDigest->strUsernameEncoded), pstrUsername);
  1698. if (!NT_SUCCESS(Status))
  1699. {
  1700. DebugLog((DEB_ERROR, "DigestCreateChalResp: Failed to copy over UsernameEncoded\n"));
  1701. goto CleanUp;
  1702. }
  1703. Status = StringReference(&(pDigest->refstrParam[MD5_AUTH_USERNAME]), &(pDigest->strUsernameEncoded));
  1704. if (!NT_SUCCESS(Status))
  1705. {
  1706. DebugLog((DEB_ERROR, "DigestCreateChalResp: Failed reference UsernameEncoded\n"));
  1707. goto CleanUp;
  1708. }
  1709. if (pUserCreds->ustrRealm.Buffer)
  1710. {
  1711. Status = EncodeUnicodeString(&pUserCreds->ustrRealm, uCodePage, &strRealm, NULL);
  1712. if (!NT_SUCCESS(Status))
  1713. {
  1714. DebugLog((DEB_WARN, "DigestCreateChalResp: Error in encoding realm\n"));
  1715. goto CleanUp;
  1716. }
  1717. Status = BackslashEncodeString(&strRealm, &strTempRealm);
  1718. if (!NT_SUCCESS (Status))
  1719. {
  1720. DebugLog((DEB_ERROR, "DigestCreateChalResp: BackslashEncode failed status 0x%x\n", Status));
  1721. goto CleanUp;
  1722. }
  1723. pstrRealm = &strTempRealm;
  1724. Status = StringDuplicate(&(pDigest->strRealmEncoded), pstrRealm);
  1725. if (!NT_SUCCESS(Status))
  1726. {
  1727. DebugLog((DEB_ERROR, "DigestCreateChalResp: Failed to copy over RealmEncoded\n"));
  1728. goto CleanUp;
  1729. }
  1730. Status = StringReference(&(pDigest->refstrParam[MD5_AUTH_REALM]), &(pDigest->strRealmEncoded));
  1731. if (!NT_SUCCESS(Status))
  1732. {
  1733. DebugLog((DEB_ERROR, "DigestCreateChalResp: Failed reference RealmEncoded\n"));
  1734. goto CleanUp;
  1735. }
  1736. }
  1737. else
  1738. {
  1739. pstrRealm = &(pDigest->refstrParam[MD5_AUTH_REALM]);
  1740. DebugLog((DEB_ERROR, "DigestCreateChalResp: Realm defaulted to Challenge realm value\n"));
  1741. }
  1742. #else // SECURITY_KERNEL
  1743. DebugLog((DEB_ERROR, "DigestCreateChalResp: User credential processing not permitted\n"));
  1744. Status = STATUS_NOT_SUPPORTED;
  1745. goto CleanUp;
  1746. #endif // SECURITY_KERNEL
  1747. }
  1748. else
  1749. {
  1750. // No usercreds passed in so just use the current digest directive values
  1751. DebugLog((DEB_WARN, "DigestCreateChalResp: No UserCredentials - use provided digest\n"));
  1752. pstrUsername = &(pDigest->refstrParam[MD5_AUTH_USERNAME]);
  1753. pstrRealm = &(pDigest->refstrParam[MD5_AUTH_REALM]);
  1754. }
  1755. // Request-URI will be % encoded backslash is an excluded character so not backslash encoding
  1756. // Precalc the amount of space needed for output
  1757. cbLenNeeded = CB_CHALRESP; // MAX byte count for directives and symbols
  1758. cbLenNeeded += pstrUsername->Length;
  1759. cbLenNeeded += pstrRealm->Length;
  1760. cbLenNeeded += pDigest->refstrParam[MD5_AUTH_NONCE].Length;
  1761. cbLenNeeded += pDigest->refstrParam[MD5_AUTH_URI].Length;
  1762. cbLenNeeded += pDigest->strResponse.Length;
  1763. cbLenNeeded += strcAlgorithm.Length;
  1764. cbLenNeeded += pDigest->refstrParam[MD5_AUTH_CNONCE].Length;
  1765. cbLenNeeded += pDigest->refstrParam[MD5_AUTH_OPAQUE].Length;
  1766. cbLenNeeded += pDigest->refstrParam[MD5_AUTH_AUTHZID].Length;
  1767. cbLenNeeded += MAX_AUTH_LENGTH;
  1768. cbLenNeeded += pDigest->refstrParam[MD5_AUTH_NC].Length;
  1769. cbLenNeeded += strcQOP.Length;
  1770. if (cbLenNeeded > NTDIGEST_SP_MAX_TOKEN_SIZE)
  1771. {
  1772. Status = STATUS_BUFFER_TOO_SMALL;
  1773. DebugLog((DEB_ERROR, "DigestCreateChalResp: output exceed max size or buffer too small len is %d\n", cbLenNeeded));
  1774. goto CleanUp;
  1775. }
  1776. // In digest calc - already checked username,realm,nonce,method,uri
  1777. // Make sure there are values for the rest needed
  1778. if ((!pDigest->strResponse.Length) ||
  1779. (!pDigest->refstrParam[MD5_AUTH_NC].Length) ||
  1780. (!pDigest->refstrParam[MD5_AUTH_CNONCE].Length))
  1781. {
  1782. // Failed on a require field-value
  1783. Status = STATUS_INVALID_PARAMETER;
  1784. DebugLog((DEB_ERROR, "DigestCreateChalResp: Response, NC, or Cnonce is zero length\n"));
  1785. goto CleanUp;
  1786. }
  1787. if (pstrRealm->Length)
  1788. {
  1789. sprintf(pczTemp,
  1790. "username=\"%Z\",realm=\"%Z\",nonce=\"%Z\",%s=\"%Z\"",
  1791. pstrUsername,
  1792. pstrRealm,
  1793. &pDigest->refstrParam[MD5_AUTH_NONCE],
  1794. ((fSASLMode == TRUE) ? DIGESTURI_STR: URI_STR),
  1795. &pDigest->refstrParam[MD5_AUTH_URI]);
  1796. }
  1797. else
  1798. {
  1799. sprintf(pczTemp,
  1800. "username=\"%Z\",realm=\"\",nonce=\"%Z\",%s=\"%Z\"",
  1801. pstrUsername,
  1802. &pDigest->refstrParam[MD5_AUTH_NONCE],
  1803. ((fSASLMode == TRUE) ? DIGESTURI_STR: URI_STR),
  1804. &pDigest->refstrParam[MD5_AUTH_URI]);
  1805. }
  1806. if (pDigest->typeQOP != NO_QOP_SPECIFIED)
  1807. {
  1808. // Do not output cnonce and nc when QOP was not specified
  1809. // this happens only for HTTP mode. In SASL mode, we default to AUTH if not specified
  1810. sprintf(pczTemp2, ",cnonce=\"%Z\",nc=%Z",
  1811. &pDigest->refstrParam[MD5_AUTH_CNONCE],
  1812. &pDigest->refstrParam[MD5_AUTH_NC]);
  1813. strcat(pczTemp, pczTemp2);
  1814. }
  1815. if (fSASLMode == TRUE)
  1816. {
  1817. // Do not output algorithm - must be md5-sess and that is assumed
  1818. sprintf(pczTemp2, ",response=%Z", &pDigest->strResponse);
  1819. strcat(pczTemp, pczTemp2);
  1820. }
  1821. else
  1822. {
  1823. if (pDigest->typeAlgorithm == MD5_SESS)
  1824. {
  1825. sprintf(pczTemp2, ",algorithm=MD5-sess,response=\"%Z\"", &pDigest->strResponse);
  1826. strcat(pczTemp, pczTemp2);
  1827. }
  1828. else
  1829. {
  1830. sprintf(pczTemp2, ",response=\"%Z\"", &pDigest->strResponse);
  1831. strcat(pczTemp, pczTemp2);
  1832. }
  1833. }
  1834. // Attach QOP if specified - support older format for no QOP
  1835. // RFC has qop not quoted, but older IIS needed this quoted.
  1836. // Set ClientCompat bit off to conform to RFC
  1837. if (strcQOP.Length)
  1838. {
  1839. if ((fSASLMode == TRUE) || (!(pDigest->usFlags & FLAG_QUOTE_QOP)))
  1840. {
  1841. sprintf(pczTemp2, ",qop=%Z", &strcQOP);
  1842. strcat(pczTemp, pczTemp2);
  1843. }
  1844. else
  1845. {
  1846. sprintf(pczTemp2, ",qop=\"%Z\"", &strcQOP);
  1847. strcat(pczTemp, pczTemp2);
  1848. }
  1849. }
  1850. // Attach Cipher selected if required
  1851. if (pDigest->typeQOP == AUTH_CONF)
  1852. {
  1853. // FIX optimize these into a list for efficiency
  1854. if (pDigest->typeCipher == CIPHER_RC4)
  1855. {
  1856. sprintf(pczTemp2, ",cipher=%s", STR_CIPHER_RC4);
  1857. strcat(pczTemp, pczTemp2);
  1858. }
  1859. else if (pDigest->typeCipher == CIPHER_RC4_56)
  1860. {
  1861. sprintf(pczTemp2, ",cipher=%s", STR_CIPHER_RC4_56);
  1862. strcat(pczTemp, pczTemp2);
  1863. }
  1864. else if (pDigest->typeCipher == CIPHER_RC4_40)
  1865. {
  1866. sprintf(pczTemp2, ",cipher=%s", STR_CIPHER_RC4_40);
  1867. strcat(pczTemp, pczTemp2);
  1868. }
  1869. else if (pDigest->typeCipher == CIPHER_3DES)
  1870. {
  1871. sprintf(pczTemp2, ",cipher=%s", STR_CIPHER_3DES);
  1872. strcat(pczTemp, pczTemp2);
  1873. }
  1874. else if (pDigest->typeCipher == CIPHER_DES)
  1875. {
  1876. sprintf(pczTemp2, ",cipher=%s", STR_CIPHER_DES);
  1877. strcat(pczTemp, pczTemp2);
  1878. }
  1879. }
  1880. // Attach opaque data (but not on SASL)
  1881. if ((fSASLMode == FALSE) && pDigest->refstrParam[MD5_AUTH_OPAQUE].Length)
  1882. {
  1883. sprintf(pczTemp2, ",opaque=\"%Z\"", &pDigest->refstrParam[MD5_AUTH_OPAQUE]);
  1884. strcat(pczTemp, pczTemp2);
  1885. }
  1886. // Attach authzid data (only in SASL mode)
  1887. if ((fSASLMode == TRUE) && (pDigest->usFlags & FLAG_AUTHZID_PROVIDED))
  1888. {
  1889. if (pDigest->refstrParam[MD5_AUTH_AUTHZID].Length)
  1890. {
  1891. sprintf(pczTemp2, ",authzid=\"%Z\"", &pDigest->refstrParam[MD5_AUTH_AUTHZID]);
  1892. }
  1893. else
  1894. {
  1895. sprintf(pczTemp2, ",authzid=\"\"");
  1896. }
  1897. strcat(pczTemp, pczTemp2);
  1898. }
  1899. // Attach charset to indicate that UTF-8 character encoding is utilized
  1900. if (pDigest->typeCharset == UTF_8)
  1901. {
  1902. strcat(pczTemp, ",charset=utf-8");
  1903. }
  1904. // total buffer for Challenge (NULL is not included in output buffer - ref:Bug 310201)
  1905. cbLenNeeded = (USHORT)strlen(pczTemp);
  1906. if (cbLenNeeded > NTDIGEST_SP_MAX_TOKEN_SIZE)
  1907. {
  1908. ASSERT(0); // This never happen since tested MAX size of cbLenNeeded above
  1909. Status = STATUS_BUFFER_TOO_SMALL;
  1910. DebugLog((DEB_ERROR, "DigestCreateChalResp: output challengeResponse too long\n"));
  1911. goto CleanUp;
  1912. }
  1913. // Check on allocating output buffer
  1914. if (!OutBuffer->cbBuffer)
  1915. {
  1916. OutBuffer->pvBuffer = DigestAllocateMemory(cbLenNeeded);
  1917. if (!OutBuffer->pvBuffer)
  1918. {
  1919. Status = SEC_E_INSUFFICIENT_MEMORY;
  1920. DebugLog((DEB_ERROR, "DigestCreateChalResp: out of memory on challenge output\n"));
  1921. goto CleanUp;
  1922. }
  1923. OutBuffer->cbBuffer = cbLenNeeded;
  1924. }
  1925. if (cbLenNeeded > OutBuffer->cbBuffer)
  1926. {
  1927. Status = STATUS_BUFFER_TOO_SMALL;
  1928. DebugLog((DEB_ERROR, "DigestCreateChalResp: output buffer too small need %d len is %d\n",
  1929. cbLenNeeded, OutBuffer->cbBuffer));
  1930. goto CleanUp;
  1931. }
  1932. memcpy(OutBuffer->pvBuffer, pczTemp, cbLenNeeded);
  1933. OutBuffer->cbBuffer = cbLenNeeded;
  1934. OutBuffer->BufferType = SECBUFFER_TOKEN;
  1935. CleanUp:
  1936. if (pczTemp)
  1937. {
  1938. DigestFreeMemory(pczTemp);
  1939. pczTemp = NULL;
  1940. }
  1941. if (pczTemp2)
  1942. {
  1943. DigestFreeMemory(pczTemp2);
  1944. pczTemp2 = NULL;
  1945. }
  1946. StringFree(&strTempRealm);
  1947. StringFree(&strTempUsername);
  1948. StringFree(&strRealm);
  1949. StringFree(&strUsername);
  1950. DebugLog((DEB_TRACE_FUNC, "DigestCreateChalResp: Leaving status 0x%x\n", Status));
  1951. return(Status);
  1952. }
  1953. NTSTATUS
  1954. DigestPrint(PDIGEST_PARAMETER pDigest)
  1955. {
  1956. NTSTATUS Status = STATUS_SUCCESS;
  1957. int i = 0;
  1958. if (!pDigest)
  1959. {
  1960. return (STATUS_INVALID_PARAMETER);
  1961. }
  1962. if (pDigest->typeDigest == DIGEST_UNDEFINED)
  1963. {
  1964. DebugLog((DEB_TRACE, "Digest: DIGEST_UNDEFINED\n"));
  1965. }
  1966. if (pDigest->typeDigest == NO_DIGEST_SPECIFIED)
  1967. {
  1968. DebugLog((DEB_ERROR, "Digest: NO_DIGEST_SPECIFIED\n"));
  1969. }
  1970. if (pDigest->typeDigest == DIGEST_CLIENT)
  1971. {
  1972. DebugLog((DEB_TRACE, "Digest: DIGEST_CLIENT\n"));
  1973. }
  1974. if (pDigest->typeDigest == DIGEST_SERVER)
  1975. {
  1976. DebugLog((DEB_TRACE, "Digest: DIGEST_SERVER\n"));
  1977. }
  1978. if (pDigest->typeDigest == SASL_SERVER)
  1979. {
  1980. DebugLog((DEB_TRACE, "Digest: SASL_SERVER\n"));
  1981. }
  1982. if (pDigest->typeDigest == SASL_CLIENT)
  1983. {
  1984. DebugLog((DEB_TRACE, "Digest: SASL_CLIENT\n"));
  1985. }
  1986. if (pDigest->typeQOP == QOP_UNDEFINED)
  1987. {
  1988. DebugLog((DEB_ERROR, "Digest: QOP: Not QOP_UNDEFINED\n"));
  1989. }
  1990. if (pDigest->typeQOP == NO_QOP_SPECIFIED)
  1991. {
  1992. DebugLog((DEB_TRACE, "Digest: QOP: Not Specified\n"));
  1993. }
  1994. if (pDigest->typeQOP == AUTH)
  1995. {
  1996. DebugLog((DEB_TRACE, "Digest: QOP: AUTH\n"));
  1997. }
  1998. if (pDigest->typeQOP == AUTH_INT)
  1999. {
  2000. DebugLog((DEB_TRACE, "Digest: QOP: AUTH_INT\n"));
  2001. }
  2002. if (pDigest->typeQOP == AUTH_CONF)
  2003. {
  2004. DebugLog((DEB_TRACE, "Digest: QOP: AUTH_CONF\n"));
  2005. }
  2006. if (pDigest->typeAlgorithm == ALGORITHM_UNDEFINED)
  2007. {
  2008. DebugLog((DEB_ERROR, "Digest: Algorithm: ALGORITHM_UNDEFINED\n"));
  2009. }
  2010. if (pDigest->typeAlgorithm == NO_ALGORITHM_SPECIFIED)
  2011. {
  2012. DebugLog((DEB_TRACE, "Digest: Algorithm: NO_ALGORITHM_SPECIFIED\n"));
  2013. }
  2014. if (pDigest->typeAlgorithm == MD5)
  2015. {
  2016. DebugLog((DEB_TRACE, "Digest: Algorithm: MD5\n"));
  2017. }
  2018. if (pDigest->typeAlgorithm == MD5_SESS)
  2019. {
  2020. DebugLog((DEB_TRACE, "Digest: Algorithm: MD5_SESS\n"));
  2021. }
  2022. if (pDigest->typeCharset == ISO_8859_1)
  2023. {
  2024. DebugLog((DEB_TRACE, "Digest: CharSet: ISO-8859-1\n"));
  2025. }
  2026. if (pDigest->typeCharset == UTF_8)
  2027. {
  2028. DebugLog((DEB_TRACE, "Digest: CharSet: UTF-8\n"));
  2029. }
  2030. if (pDigest->usFlags & FLAG_CRACKNAME_ON_DC)
  2031. {
  2032. DebugLog((DEB_TRACE, "Digest: Flags: CrackName on DC\n"));
  2033. }
  2034. if (pDigest->usFlags & FLAG_AUTHZID_PROVIDED)
  2035. {
  2036. DebugLog((DEB_TRACE, "Digest: Flags: AuthzID info provided\n"));
  2037. }
  2038. if (pDigest->usFlags & FLAG_SERVERS_DOMAIN)
  2039. {
  2040. DebugLog((DEB_TRACE, "Digest: Flags: Server's DC\n"));
  2041. }
  2042. if (pDigest->usFlags & FLAG_BS_ENCODE_CLIENT_BROKEN)
  2043. {
  2044. DebugLog((DEB_TRACE, "Digest: Flags: Client BS encode compatibility\n"));
  2045. }
  2046. if (pDigest->usFlags & FLAG_AUTHZID_PROVIDED)
  2047. {
  2048. DebugLog((DEB_TRACE, "Digest: Flags: AuthzID provided\n"));
  2049. }
  2050. if (pDigest->usFlags & FLAG_NOBS_DECODE)
  2051. {
  2052. DebugLog((DEB_TRACE, "Digest: Flags: No Backslash Decoding\n"));
  2053. }
  2054. for (i=0; i < MD5_AUTH_LAST;i++)
  2055. {
  2056. if (pDigest->refstrParam[i].Buffer &&
  2057. pDigest->refstrParam[i].Length)
  2058. {
  2059. DebugLog((DEB_TRACE, "Digest: Digest[%d] = \"%Z\"\n", i, &pDigest->refstrParam[i]));
  2060. }
  2061. }
  2062. DebugLog((DEB_TRACE, "Digest: SessionKey %Z\n", &(pDigest->strSessionKey)));
  2063. DebugLog((DEB_TRACE, "Digest: Response %Z\n", &(pDigest->strResponse)));
  2064. DebugLog((DEB_TRACE, "Digest: Username %wZ\n", &(pDigest->ustrUsername)));
  2065. DebugLog((DEB_TRACE, "Digest: Realm %wZ\n", &(pDigest->ustrRealm)));
  2066. DebugLog((DEB_TRACE, "Digest: CrackedAccountName %wZ\n", &(pDigest->ustrCrackedAccountName)));
  2067. DebugLog((DEB_TRACE, "Digest: CrackedDomain %wZ\n", &(pDigest->ustrCrackedDomain)));
  2068. DebugLog((DEB_TRACE, "Digest: Workstation %wZ\n", &(pDigest->ustrWorkstation)));
  2069. return(Status);
  2070. }