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.

4797 lines
182 KiB

  1. //+-----------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (c) Microsoft Corporation 2000
  6. //
  7. // File: ctxtapi.cxx
  8. //
  9. // Contents: Context APIs for the Digest security package
  10. // Main entry points into this dll:
  11. // SpDeleteContext
  12. // SpInitLsaModeContext
  13. // SpApplyControlToken
  14. // SpAcceptLsaModeContext
  15. //
  16. // History: KDamour 16Mar00 Based from NTLM ctxtapi.cxx
  17. //
  18. //------------------------------------------------------------------------
  19. extern "C"
  20. {
  21. #include <stdio.h>
  22. }
  23. #include "global.h"
  24. extern "C"
  25. {
  26. #include <ntdsapi.h> // DS_USER_PRINCIPAL_NAME
  27. #include <ntdsa.h> // CrackSingleName
  28. #include <lsarpc.h>
  29. #include <lsaisrv.h> // LsaIIsDomainWithinForest
  30. #include <align.h> // ROUND_UP_COUNT
  31. }
  32. #define MAXBUFNUMLEN 9 // VERY BIG number of digits in maxbuf
  33. #define SECONDS_TO_100NANO 10000000 // Convert 100 nanoseconds to seconds
  34. //+-------------------------------------------------------------------------
  35. //
  36. // Function: SpDeleteContext
  37. //
  38. // Synopsis: Deletes an NtDigest context
  39. //
  40. // Deletes the local data structures associated with the specified
  41. // security context in the LSA.
  42. //
  43. // This API terminates a context on the local machine.
  44. //
  45. // Effects:
  46. //
  47. // Arguments: ContextHandle - The context to delete
  48. //
  49. // Requires:
  50. //
  51. // Returns: STATUS_SUCCESS or STATUS_INVALID_HANDLE
  52. //
  53. // Notes: This call is made ONCE to signal the SSP that the LSA is removing this context.
  54. // There might be outstanding requests in process so the reference count will be decremented
  55. //
  56. //
  57. //--------------------------------------------------------------------------
  58. NTSTATUS NTAPI
  59. SpDeleteContext(
  60. IN ULONG_PTR ContextHandle
  61. )
  62. {
  63. DebugLog((DEB_TRACE_FUNC, "SpDeleteContext: Entering ContextHandle 0x%lx\n", ContextHandle ));
  64. PDIGEST_CONTEXT pContext = NULL;
  65. NTSTATUS Status = STATUS_SUCCESS;
  66. ULONG ulDereferenceCount = 1;
  67. SECPKG_CALL_INFO CallInfo = {0};
  68. //
  69. // Find the currently existing user context and delink it
  70. // so that another context cannot Reference it before we
  71. // Dereference this one.
  72. //
  73. Status = CtxtHandlerHandleToContext(ContextHandle, TRUE, &pContext);
  74. if (!NT_SUCCESS(Status))
  75. {
  76. Status = STATUS_SUCCESS;
  77. DebugLog((DEB_TRACE, "SpDeleteContext: CtxtHandlerHandleToContext not found 0x%x\n", Status ));
  78. goto CleanUp;
  79. }
  80. // Need to determine how many reference counts to decrement by - the LSA kept track too
  81. // This is overly complex logic in that both the LSA and the SSP keep track of the SecurityContext
  82. // handle reference counts
  83. if(g_LsaFunctions->GetCallInfo(&CallInfo))
  84. {
  85. ulDereferenceCount = CallInfo.CallCount;
  86. DebugLog((DEB_TRACE, "SpDeleteContext: CallCount for references 0x%x\n", CallInfo.CallCount));
  87. DebugLog((DEB_TRACE, "SpDeleteContext: Attributes 0x%x\n", CallInfo.Attributes));
  88. DebugLog((DEB_TRACE, "SpDeleteContext: PID %d Thread %d\n", CallInfo.ProcessId, CallInfo.ThreadId));
  89. }
  90. // Now deference - there may be other references from pointer references (from Handles)
  91. // inside the LSA but will be released
  92. if (pContext)
  93. {
  94. Status = CtxtHandlerRelease(pContext, ulDereferenceCount);
  95. if (!NT_SUCCESS(Status))
  96. {
  97. DebugLog((DEB_ERROR, "SpDeleteContext: DereferenceUserContext error Status 0x%x\n", Status ));
  98. }
  99. }
  100. CleanUp:
  101. DebugLog((DEB_TRACE_FUNC, "SpDeleteContext: Leaving ContextHandle 0x%lx status 0x%x\n",
  102. ContextHandle, Status ));
  103. return(Status);
  104. }
  105. //+-------------------------------------------------------------------------
  106. //
  107. // Function: SpInitLsaModeContext
  108. //
  109. // Synopsis: Digest implementation of InitializeSecurityContext
  110. // while in Lsa mode. If we return TRUE in *MappedContext,
  111. // secur32 will call SpInitUserModeContext with
  112. // the returned context handle and ContextData
  113. // as input. Fill in whatever info needed for
  114. // the user mode APIs
  115. //
  116. // Effects:
  117. //
  118. // Arguments:
  119. //
  120. // Requires:
  121. //
  122. // Returns:
  123. //
  124. // Notes:
  125. //
  126. //--------------------------------------------------------------------------
  127. NTSTATUS NTAPI
  128. SpInitLsaModeContext(
  129. IN OPTIONAL ULONG_PTR CredentialHandle,
  130. IN OPTIONAL ULONG_PTR OldContextHandle,
  131. IN OPTIONAL PUNICODE_STRING pustrTargetName,
  132. IN ULONG fContextReqFlags,
  133. IN ULONG TargetDataRep,
  134. IN PSecBufferDesc InputBuffers,
  135. OUT PULONG_PTR NewContextHandle,
  136. IN OUT PSecBufferDesc OutputBuffers,
  137. OUT PULONG fContextAttributes,
  138. OUT PTimeStamp pExpirationTime,
  139. OUT PBOOLEAN MappedContext,
  140. OUT PSecBuffer ContextData
  141. )
  142. {
  143. NTSTATUS Status = STATUS_SUCCESS;
  144. NTSTATUS SubStatus = STATUS_SUCCESS;
  145. DebugLog((DEB_TRACE_FUNC, "SpInitLsaModeContext: Entering ContextHandle 0x%x\n", OldContextHandle));
  146. SecBuffer TempTokens[6];
  147. PSecBuffer pChalInputToken;
  148. PSecBuffer pMethodInputToken;
  149. PSecBuffer pHEntityInputToken;
  150. PSecBuffer pOutputToken;
  151. DIGEST_PARAMETER Digest;
  152. PDIGEST_CONTEXT pNewContext = NULL; // keep pointer to release new context on error
  153. PDIGEST_CONTEXT pContext = NULL; // used to update the context
  154. BOOL bLockedContext = FALSE; // if we obtained a refcount on a Context
  155. BOOL fDefChars = FALSE; // were default chars utilized in Unicode encoding
  156. USHORT usLen = 0;
  157. SecBuffer ReplyBuffer; // Output is generated in this buffer
  158. ULONG fContextAttr = ISC_REQ_REPLAY_DETECT; // Flags on the Attributes of the context
  159. DIGEST_TYPE typeDigest = NO_DIGEST_SPECIFIED;
  160. QOP_TYPE typeQOP = NO_QOP_SPECIFIED;
  161. ALGORITHM_TYPE typeAlgorithm = NO_ALGORITHM_SPECIFIED;
  162. CHARSET_TYPE typeCharset = ISO_8859_1;
  163. DIGESTMODE_TYPE typeDigestMode = DIGESTMODE_UNDEFINED; // are we in SASL or HTTP mode
  164. PDIGEST_CREDENTIAL pCredential = NULL;
  165. STRING strcSASLMethod = {0};
  166. STRING strcSASLHEntity = {0};
  167. STRING strcNC = {0};
  168. STRING strTargetName = {0};
  169. UNREFERENCED_PARAMETER(TargetDataRep);
  170. // Verify Args
  171. if (!fContextAttributes || !NewContextHandle || !OutputBuffers)
  172. {
  173. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Invalid arg (possible NULL pointer)\n"));
  174. return STATUS_INVALID_PARAMETER;
  175. }
  176. *fContextAttributes = 0;
  177. *NewContextHandle = NULL;
  178. if (pExpirationTime)
  179. {
  180. *pExpirationTime = g_TimeForever;
  181. }
  182. *MappedContext = FALSE;
  183. ContextData->pvBuffer = NULL;
  184. ContextData->cbBuffer = 0;
  185. // Create pointers to tokens for processing
  186. pChalInputToken = &TempTokens[0];
  187. pMethodInputToken = &TempTokens[1];
  188. pHEntityInputToken = &TempTokens[3];
  189. pOutputToken = &TempTokens[4];
  190. ZeroMemory(TempTokens,sizeof(TempTokens));
  191. ZeroMemory(&ReplyBuffer, sizeof(ReplyBuffer));
  192. Status = DigestInit(&Digest);
  193. if (!NT_SUCCESS(Status))
  194. {
  195. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Digest init error status 0x%x\n", Status));
  196. goto CleanUp;
  197. }
  198. // Must have a Credential Handle to perform processing - will ref count
  199. Status = CredHandlerHandleToPtr(CredentialHandle, FALSE, &pCredential);
  200. if (!NT_SUCCESS(Status))
  201. {
  202. Status = SEC_E_UNKNOWN_CREDENTIALS;
  203. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Could not locate the Credential\n"));
  204. goto CleanUp;
  205. }
  206. // Verify that credential is marked OUTBOUND for ASC call
  207. if (!(pCredential->CredentialUseFlags & DIGEST_CRED_OUTBOUND))
  208. {
  209. Status = SEC_E_NOT_SUPPORTED;
  210. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Credential not marked for Outbound connections\n"));
  211. goto CleanUp;
  212. }
  213. // Retrieve the information from the SecBuffers & check proper formattting
  214. // Check for NULL input for InputBuffers - as is done for 1st call to ISC
  215. if (InputBuffers && (InputBuffers->cBuffers))
  216. {
  217. if ( !SspGetTokenBufferByIndex( InputBuffers,
  218. 0, // get the first SECBUFFER_TOKEN
  219. &pChalInputToken,
  220. TRUE ) ||
  221. !ContextIsTokenOK(pChalInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE))
  222. {
  223. Status = SEC_E_INVALID_TOKEN;
  224. DebugLog((DEB_ERROR, "SpInitLsaModeContext: SspGetTokenBufferByIndex (ChalRspInputToken) status 0x%x\n", Status));
  225. goto CleanUp;
  226. }
  227. }
  228. // Process the output buffer
  229. if ( !SspGetTokenBufferByIndex( OutputBuffers,
  230. 0, // get the first SECBUFFER_TOKEN
  231. &pOutputToken,
  232. FALSE ) ||
  233. !ContextIsTokenOK(pOutputToken, 0))
  234. {
  235. Status = SEC_E_INVALID_TOKEN;
  236. DebugLog((DEB_ERROR, "SpInitLsaModeContext: SspGetTokenBufferByIndex (OutputToken) status 0x%x\n", Status));
  237. goto CleanUp;
  238. }
  239. if (fContextReqFlags & ISC_REQ_ALLOCATE_MEMORY)
  240. {
  241. pOutputToken->pvBuffer = NULL;
  242. pOutputToken->cbBuffer = 0;
  243. }
  244. // To support SASL's call to ISC BEFORE any calls to ASC just return SEC_I_CONTINUE_NEEDED
  245. if (pChalInputToken->cbBuffer <= 1)
  246. {
  247. // Need to create a context for this connection - destroy if unsuccessful auth
  248. pNewContext = (PDIGEST_CONTEXT)DigestAllocateMemory(sizeof(DIGEST_CONTEXT));
  249. if (!pNewContext)
  250. {
  251. Status = SEC_E_INSUFFICIENT_MEMORY;
  252. DebugLog((DEB_ERROR, "SpInitLsaModeContext: ISC empty context - Out of memory on challenge context\n"));
  253. goto CleanUp;
  254. }
  255. CredPrint(pCredential);
  256. // Initialize new context
  257. Status = ContextInit(pNewContext, pCredential);
  258. if (!NT_SUCCESS (Status))
  259. {
  260. DebugLog((DEB_ERROR, "SpInitLsaModeContext: ISC empty context - ContextInit error status 0x%x\n", Status));
  261. goto CleanUp;
  262. }
  263. Status = SEC_I_CONTINUE_NEEDED; // Have no input for processing
  264. pOutputToken->cbBuffer = 0; // No output buffer
  265. DebugLog((DEB_TRACE, "SpInitLsaModeContext: ISC empty context - Called with no Input Buffer Status 0x%x\n", Status));
  266. // Add the Newly created Context into the list of Contexts
  267. pNewContext->lReferences = 1; // pass reference back to ISC caller
  268. CtxtHandlerInsertCred(pNewContext);
  269. // pContext = pNewContext; // set to have dereferenced
  270. *NewContextHandle = (ULONG_PTR)pNewContext; // Just report back with the updated context
  271. *fContextAttributes = fContextAttr; // Return the ISC Attributes set on Context
  272. // bLockedContext = TRUE; // Release memory to CtxtHandler
  273. pNewContext = NULL; // We no longer own this memory - turned over to CtxtHandler
  274. goto CleanUp;
  275. }
  276. // Verify SecBuffer inputs - both SASL and HTTP require atleast 1 buffer
  277. if (!InputBuffers || !InputBuffers->cBuffers)
  278. {
  279. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Invalid SecBufferDesc\n"));
  280. return STATUS_INVALID_PARAMETER;
  281. }
  282. // We have input in the SECBUFFER 0th location - parse it
  283. Status = DigestParser2(pChalInputToken, MD5_AUTH_NAMES, MD5_AUTH_LAST, &Digest);
  284. if (!NT_SUCCESS(Status))
  285. {
  286. DebugLog((DEB_ERROR, "SpInitLsaModeContext: DigestParser error status 0x%x\n", Status));
  287. goto CleanUp;
  288. }
  289. // Check to see if we have an old context passed in or need to create a new one
  290. if (OldContextHandle)
  291. {
  292. // Old Context passed in - locate the security context and use that
  293. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Passed in OldContextHandle 0x%lx - lookup in list\n", OldContextHandle));
  294. Status = CtxtHandlerHandleToContext(OldContextHandle, FALSE, &pContext);
  295. if (!NT_SUCCESS (Status))
  296. {
  297. Status = SEC_E_INVALID_TOKEN;
  298. DebugLog((DEB_ERROR, "SpInitLsaModeContext: CtxtHandlerHandleToContext error 0x%x\n", Status));
  299. goto CleanUp;
  300. }
  301. bLockedContext = TRUE;
  302. }
  303. else
  304. {
  305. // Need to create a context for this connection - destroy if unsuccessful auth
  306. DebugLog((DEB_TRACE, "SpInitLsaModeContext: No OldContextHandle - create new Context\n"));
  307. pNewContext = (PDIGEST_CONTEXT)DigestAllocateMemory(sizeof(DIGEST_CONTEXT));
  308. if (!pNewContext)
  309. {
  310. Status = SEC_E_INSUFFICIENT_MEMORY;
  311. DebugLog((DEB_ERROR, "SpInitLsaModeContext: out of memory on challenge context\n"));
  312. goto CleanUp;
  313. }
  314. CredPrint(pCredential);
  315. // Initialize new context
  316. Status = ContextInit(pNewContext, pCredential);
  317. if (!NT_SUCCESS (Status))
  318. {
  319. DebugLog((DEB_ERROR, "SpInitLsaModeContext: ContextInit error status 0x%x\n", Status));
  320. goto CleanUp;
  321. }
  322. pContext = pNewContext; // for filling in the context information
  323. DebugLog((DEB_TRACE, "SpInitLsaModeContext: New Context Created 0x%x\n", pContext));
  324. }
  325. if (pContext && pContext->strResponseAuth.Length)
  326. {
  327. // We have already generated session key from challenge response
  328. // now checking response auth from server
  329. if (Digest.refstrParam[MD5_AUTH_RSPAUTH].Length != MD5_HASH_HEX_SIZE)
  330. {
  331. Status = STATUS_MUTUAL_AUTHENTICATION_FAILED;
  332. DebugLog((DEB_ERROR, "SpInitLsaModeContext: RspAuth incorrect size\n"));
  333. goto CleanUp;
  334. }
  335. // Now compare the response auth strings
  336. if (!RtlEqualString(&(pContext->strResponseAuth),
  337. &(Digest.refstrParam[MD5_AUTH_RSPAUTH]),
  338. FALSE))
  339. {
  340. Status = STATUS_MUTUAL_AUTHENTICATION_FAILED;
  341. DebugLog((DEB_ERROR, "SpInitLsaModeContext: RspAuth is incorrect\n"));
  342. goto CleanUp;
  343. }
  344. DebugLog((DEB_TRACE, "SpInitLsaModeContext: RspAuth matches!\n"));
  345. // ResponseAuth is verified - generate mapped context
  346. *fContextAttributes = pContext->ContextReq; // Return the ISC Attributes set on Context
  347. *NewContextHandle = (ULONG_PTR)pContext; // Just report back with the updated context
  348. pOutputToken->cbBuffer = 0; // No output buffer
  349. if (pExpirationTime)
  350. {
  351. *pExpirationTime = pContext->ExpirationTime;
  352. }
  353. Status = SspMapDigestContext(pContext, NULL, FLAG_CONTEXT_REFCOUNT, ContextData);
  354. if (!NT_SUCCESS(Status))
  355. {
  356. DebugLog((DEB_ERROR, "SpInitLsaModeContext, SspMapContext Status 0x%x\n", Status));
  357. goto CleanUp;
  358. }
  359. // this is final call, indicate to map the context
  360. *MappedContext = TRUE;
  361. goto CleanUp;
  362. }
  363. // Determine if we are in HTTP or SASL mode
  364. // SASL mode has 1 or less buffers provided, HTTP has 3
  365. if (InputBuffers->cBuffers > 1)
  366. {
  367. typeDigestMode = DIGESTMODE_HTTP;
  368. }
  369. else
  370. {
  371. typeDigestMode = DIGESTMODE_SASL;
  372. }
  373. // HTTP has special Buffer needs in that it must pass in the METHOD, HEntity
  374. if (typeDigestMode == DIGESTMODE_HTTP)
  375. {
  376. if ( !SspGetTokenBufferByIndex( InputBuffers,
  377. 1, // get the second SECBUFFER_TOKEN
  378. &pMethodInputToken,
  379. TRUE ) ||
  380. !ContextIsTokenOK(pMethodInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE))
  381. { // Check to make sure that string is present
  382. Status = SEC_E_INVALID_TOKEN;
  383. DebugLog((DEB_ERROR, "SpInitLsaModeContext: SspGetTokenBufferByIndex (MethodInputToken) status 0x%x\n", Status));
  384. goto CleanUp;
  385. }
  386. if ( !SspGetTokenBufferByIndex( InputBuffers,
  387. 2, // get the third SECBUFFER_TOKEN
  388. &pHEntityInputToken,
  389. TRUE ) ||
  390. !ContextIsTokenOK(pHEntityInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE))
  391. {
  392. Status = SEC_E_INVALID_TOKEN;
  393. DebugLog((DEB_ERROR, "SpInitLsaModeContext: SspGetTokenBufferByIndex (HEntityInputToken) status 0x%x\n", Status));
  394. goto CleanUp;
  395. }
  396. // Verify that there is a valid Method provided
  397. if (!pMethodInputToken->pvBuffer || !pMethodInputToken->cbBuffer ||
  398. (PBUFFERTYPE(pMethodInputToken) != SECBUFFER_PKG_PARAMS))
  399. {
  400. Status = SEC_E_INVALID_TOKEN;
  401. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Method SecBuffer must have valid method string status 0x%x\n", Status));
  402. goto CleanUp;
  403. }
  404. usLen = strlencounted((char *)pMethodInputToken->pvBuffer, (USHORT)pMethodInputToken->cbBuffer);
  405. if (!usLen)
  406. {
  407. Status = SEC_E_INVALID_TOKEN;
  408. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Method SecBuffer must have valid method string status 0x%x\n", Status));
  409. goto CleanUp;
  410. }
  411. Digest.refstrParam[MD5_AUTH_METHOD].Length = usLen;
  412. Digest.refstrParam[MD5_AUTH_METHOD].MaximumLength = (unsigned short)(pMethodInputToken->cbBuffer);
  413. Digest.refstrParam[MD5_AUTH_METHOD].Buffer = (char *)pMethodInputToken->pvBuffer; // refernce memory - no alloc!!!!
  414. // Check to see if we have H(Entity) data to utilize
  415. if (pHEntityInputToken->cbBuffer)
  416. {
  417. // Verify that there is a valid Method provided
  418. if (!pHEntityInputToken->pvBuffer || (PBUFFERTYPE(pMethodInputToken) != SECBUFFER_PKG_PARAMS))
  419. {
  420. Status = SEC_E_INVALID_TOKEN;
  421. DebugLog((DEB_ERROR, "SpInitLsaModeContext: HEntity SecBuffer must have valid string status 0x%x\n", Status));
  422. goto CleanUp;
  423. }
  424. usLen = strlencounted((char *)pHEntityInputToken->pvBuffer, (USHORT)pHEntityInputToken->cbBuffer);
  425. if ((usLen != 0) && (usLen != (MD5_HASH_BYTESIZE * 2)))
  426. {
  427. Status = SEC_E_INVALID_TOKEN;
  428. DebugLog((DEB_ERROR, "SpInitLsaModeContext: HEntity SecBuffer must have valid MD5 Hash data 0x%x\n", Status));
  429. goto CleanUp;
  430. }
  431. if (usLen)
  432. {
  433. Digest.refstrParam[MD5_AUTH_HENTITY].Length = usLen;
  434. Digest.refstrParam[MD5_AUTH_HENTITY].MaximumLength = (unsigned short)(pHEntityInputToken->cbBuffer);
  435. Digest.refstrParam[MD5_AUTH_HENTITY].Buffer = (char *)pHEntityInputToken->pvBuffer; // refernce memory - no alloc!!!!
  436. }
  437. }
  438. typeDigest = DIGEST_CLIENT;
  439. // Determine which Algorithm to support under HTTP
  440. Status = CheckItemInList(MD5_SESSSTR, &(Digest.refstrParam[MD5_AUTH_ALGORITHM]), FALSE);
  441. if (!NT_SUCCESS(Status))
  442. {
  443. // Check if MD5 specified (or none specified so MD5 defaults)
  444. Status = CheckItemInList(MD5STR, &(Digest.refstrParam[MD5_AUTH_ALGORITHM]), FALSE);
  445. if (NT_SUCCESS(Status) || (Digest.refstrParam[MD5_AUTH_ALGORITHM].Length == 0))
  446. {
  447. typeAlgorithm = MD5;
  448. fContextAttr |= (ISC_RET_REPLAY_DETECT | ISC_RET_SEQUENCE_DETECT);
  449. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Server allows MD5 (or defaulted); selected as algorithm\n"));
  450. }
  451. else
  452. {
  453. Status = SEC_E_QOP_NOT_SUPPORTED;
  454. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Unknown Server algorithms provided\n"));
  455. goto CleanUp;
  456. }
  457. }
  458. else
  459. {
  460. typeAlgorithm = MD5_SESS;
  461. fContextAttr |= (ISC_RET_REPLAY_DETECT | ISC_RET_SEQUENCE_DETECT);
  462. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Server allows MD5-sess; selected as algorithm\n"));
  463. }
  464. }
  465. else
  466. {
  467. // All others follow the SASL Interface so there are default values
  468. typeDigest = SASL_CLIENT;
  469. fContextAttr |= ISC_RET_MUTUAL_AUTH; // require response auth from server
  470. // SASL supports only MD5-Sess verify that server offered this
  471. Status = CheckItemInList(MD5_SESS_SASLSTR, &(Digest.refstrParam[MD5_AUTH_ALGORITHM]), FALSE);
  472. if (!NT_SUCCESS(Status))
  473. {
  474. Status = SEC_E_QOP_NOT_SUPPORTED;
  475. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Unknown Server algorithm provided\n"));
  476. goto CleanUp;
  477. }
  478. else
  479. {
  480. typeAlgorithm = MD5_SESS;
  481. fContextAttr |= (ISC_RET_REPLAY_DETECT | ISC_RET_SEQUENCE_DETECT);
  482. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Server allows MD5-sess; selected as algorithm\n"));
  483. }
  484. // Set Method to Authenticate
  485. RtlInitString(&strcSASLMethod, AUTHENTICATESTR);
  486. StringReference(&(Digest.refstrParam[MD5_AUTH_METHOD]), &strcSASLMethod); // refernce memory - no alloc!!!!
  487. RtlInitString(&strcSASLHEntity, ZERO32STR);
  488. StringReference(&(Digest.refstrParam[MD5_AUTH_HENTITY]), &strcSASLHEntity); // refernce memory - no alloc!!!!
  489. }
  490. // Determine if we can process the QOP specified - check return in client if consistent
  491. if (fContextReqFlags & ISC_REQ_CONFIDENTIALITY)
  492. {
  493. // make sure that server presented the auth-conf option
  494. Status = CheckItemInList(AUTHCONFSTR, &(Digest.refstrParam[MD5_AUTH_QOP]), FALSE);
  495. if (!NT_SUCCESS(Status))
  496. {
  497. // Failed to provide necessary QOP
  498. Status = SEC_E_QOP_NOT_SUPPORTED;
  499. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Server failed to provide QOP=AUTH-CONF\n"));
  500. goto CleanUp;
  501. }
  502. // OK agreed to QOP
  503. fContextAttr |= (ISC_RET_CONFIDENTIALITY | ISC_RET_INTEGRITY);
  504. typeQOP = AUTH_CONF;
  505. }
  506. else if (fContextReqFlags & ISC_REQ_INTEGRITY)
  507. {
  508. // make sure that server presented the auth-int option
  509. Status = CheckItemInList(AUTHINTSTR, &(Digest.refstrParam[MD5_AUTH_QOP]), FALSE);
  510. if (!NT_SUCCESS(Status))
  511. {
  512. // Failed to provide necessary QOP
  513. Status = SEC_E_QOP_NOT_SUPPORTED;
  514. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Server failed to provide QOP=AUTH-INT\n"));
  515. goto CleanUp;
  516. }
  517. // OK agreed to QOP
  518. fContextAttr |= ISC_RET_INTEGRITY;
  519. typeQOP = AUTH_INT;
  520. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Client selected QOP=AUTH-INT\n"));
  521. }
  522. else
  523. {
  524. // no client specified QOP so use auth if allowed (backwards compat may have no QOP presented from server)
  525. Status = CheckItemInList(AUTHSTR, &(Digest.refstrParam[MD5_AUTH_QOP]), FALSE);
  526. if (!NT_SUCCESS(Status))
  527. {
  528. // either QOP is not specified or all options are unknown
  529. if (Digest.refstrParam[MD5_AUTH_QOP].Length == 0)
  530. {
  531. if (typeDigestMode == DIGESTMODE_HTTP)
  532. {
  533. // Backwards compatibility with RFC 2069
  534. typeQOP = NO_QOP_SPECIFIED;
  535. DebugLog((DEB_TRACE, "SpInitLsaModeContext: No QOP specified - back compat with RFC 2069\n"));
  536. }
  537. else
  538. {
  539. // in SASL mode - if no qop specified, defaults to AUTH
  540. typeQOP = AUTH;
  541. DebugLog((DEB_TRACE, "SpInitLsaModeContext: no QOP specified - default to AUTH in SASL mode\n"));
  542. }
  543. }
  544. else
  545. {
  546. Status = SEC_E_QOP_NOT_SUPPORTED;
  547. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Server failed to provide QOP=AUTH\n"));
  548. goto CleanUp;
  549. }
  550. }
  551. else
  552. {
  553. // defaulting to AUTH
  554. typeQOP = AUTH;
  555. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Client selected QOP=AUTH by default\n"));
  556. }
  557. }
  558. // Check to see if the Server has provided character set for encoding - only UTF-8 accepted
  559. Status = CheckItemInList(MD5_UTF8STR, &(Digest.refstrParam[MD5_AUTH_CHARSET]), TRUE);
  560. if (NT_SUCCESS(Status))
  561. {
  562. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Server allows UTF-8 encoding\n"));
  563. // Determine which character set to utilize
  564. if (((typeDigest == SASL_CLIENT) && (g_fParameter_UTF8SASL == TRUE)) ||
  565. ((typeDigest == DIGEST_CLIENT) && (g_fParameter_UTF8HTTP == TRUE)))
  566. {
  567. typeCharset = UTF_8;
  568. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Selected UTF-8 encoding\n"));
  569. }
  570. else
  571. {
  572. typeCharset = ISO_8859_1;
  573. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Selected ISO 8859-1 encoding\n"));
  574. }
  575. }
  576. // Pull in the URI provided in TargetName - replace any value in challenge string - link ONLY no allocate
  577. if (!pustrTargetName)
  578. {
  579. Status = SEC_E_TARGET_UNKNOWN;
  580. DebugLog((DEB_ERROR, "SpInitLsaModeContext: URI TargetName must have valid UnicodeString\n"));
  581. goto CleanUp;
  582. }
  583. Status = EncodeUnicodeString(pustrTargetName, CP_8859_1, &strTargetName, &fDefChars);
  584. if (!NT_SUCCESS(Status))
  585. {
  586. DebugLog((DEB_WARN, "SpInitLsaModeContext: Error in encoding target URI in ISO-8859-1\n"));
  587. goto CleanUp;
  588. }
  589. if (fDefChars == TRUE)
  590. {
  591. // We could not encode the provided target URI within ISO 8859-1 characters
  592. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Target URI can not be encoded in ISO 8859-1\n"));
  593. Status = STATUS_UNMAPPABLE_CHARACTER;
  594. goto CleanUp;
  595. }
  596. StringReference(&(Digest.refstrParam[MD5_AUTH_URI]), &strTargetName); // refernce memory - no alloc!!!!
  597. // Create the CNonce
  598. Status = OpaqueCreate(&(pContext->strCNonce));
  599. if (!NT_SUCCESS (Status))
  600. {
  601. DebugLog((DEB_ERROR, "SpInitLsaModeContext: OpaqueCreate for CNonce status 0x%x\n", Status));
  602. goto CleanUp;
  603. }
  604. // Establish the Client Nonce
  605. StringReference(&(Digest.refstrParam[MD5_AUTH_CNONCE]), &(pContext->strCNonce)); // refernce memory - no alloc!!!!
  606. // Keep a copy of the Nonce and Cnonce for future Delegation requests (actually not used in client ISC)
  607. Status = StringDuplicate(&pContext->strNonce, &Digest.refstrParam[MD5_AUTH_NONCE]);
  608. if (!NT_SUCCESS (Status))
  609. {
  610. DebugLog((DEB_ERROR, "SpInitLsaModeContext: StringDuplicate CNonce failed status 0x%x\n", Status));
  611. goto CleanUp;
  612. }
  613. RtlInitString(&strcNC, NCFIRST);
  614. StringReference(&(Digest.refstrParam[MD5_AUTH_NC]), &strcNC); // refernce memory - no alloc!!!!
  615. // Set the type of Digest Parameters we are to process
  616. pContext->typeDigest = typeDigest;
  617. pContext->typeAlgorithm = typeAlgorithm;
  618. pContext->typeQOP = typeQOP;
  619. pContext->typeCipher = CIPHER_UNDEFINED;
  620. pContext->typeCharset = typeCharset; // Digest parameter will be set in DigestGenerateParameters call
  621. if (pContext->typeQOP == AUTH_CONF)
  622. {
  623. // Check if server offered RC4 Most cases this will be the cipher selected
  624. Status = CheckItemInList(STR_CIPHER_RC4, &(Digest.refstrParam[MD5_AUTH_CIPHER]), FALSE);
  625. if (NT_SUCCESS(Status))
  626. {
  627. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Selecting RC4 for auth-conf cipher\n"));
  628. pContext->typeCipher = CIPHER_RC4;
  629. }
  630. else
  631. {
  632. Status = CheckItemInList(STR_CIPHER_3DES, &(Digest.refstrParam[MD5_AUTH_CIPHER]), FALSE);
  633. if (NT_SUCCESS(Status))
  634. {
  635. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Selecting Triple DES for auth-conf cipher\n"));
  636. pContext->typeCipher = CIPHER_3DES;
  637. }
  638. else
  639. {
  640. Status = CheckItemInList(STR_CIPHER_RC4_56, &(Digest.refstrParam[MD5_AUTH_CIPHER]), FALSE);
  641. if (NT_SUCCESS(Status))
  642. {
  643. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Selecting RC4-56 for auth-conf cipher\n"));
  644. pContext->typeCipher = CIPHER_RC4_56;
  645. }
  646. else
  647. {
  648. Status = CheckItemInList(STR_CIPHER_RC4_40, &(Digest.refstrParam[MD5_AUTH_CIPHER]), FALSE);
  649. if (NT_SUCCESS(Status))
  650. {
  651. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Selecting RC4-40 for auth-conf cipher\n"));
  652. pContext->typeCipher = CIPHER_RC4_40;
  653. }
  654. else
  655. {
  656. Status = CheckItemInList(STR_CIPHER_DES, &(Digest.refstrParam[MD5_AUTH_CIPHER]), FALSE);
  657. if (NT_SUCCESS(Status))
  658. {
  659. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Selecting DES for auth-conf cipher\n"));
  660. pContext->typeCipher = CIPHER_DES;
  661. }
  662. else
  663. {
  664. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Failed to find known ciper in list\n"));
  665. Status = STATUS_CRYPTO_SYSTEM_INVALID;
  666. goto CleanUp;
  667. }
  668. }
  669. }
  670. }
  671. }
  672. }
  673. // Check if server specified a MaxBuffer
  674. if (Digest.refstrParam[MD5_AUTH_MAXBUF].Length && Digest.refstrParam[MD5_AUTH_MAXBUF].Buffer)
  675. {
  676. if (Digest.refstrParam[MD5_AUTH_MAXBUF].Length < MAXBUFNUMLEN)
  677. {
  678. ULONG ulMaxBuf = 0;
  679. CHAR czMaxBuf[MAXBUFNUMLEN + 1];
  680. ZeroMemory(czMaxBuf, (MAXBUFNUMLEN + 1));
  681. memcpy(czMaxBuf, Digest.refstrParam[MD5_AUTH_MAXBUF].Buffer, Digest.refstrParam[MD5_AUTH_MAXBUF].Length);
  682. Status = RtlCharToInteger(czMaxBuf, TENBASE, &ulMaxBuf);
  683. if (!NT_SUCCESS(Status))
  684. {
  685. Status = SEC_E_ILLEGAL_MESSAGE;
  686. DebugLog((DEB_ERROR, "SpInitLsaModeContext: MaxBuf directive value malformed 0x%x\n", Status));
  687. goto CleanUp;
  688. }
  689. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Server MaxBuf set to %lu\n", ulMaxBuf));
  690. pContext->ulSendMaxBuf = ulMaxBuf;
  691. }
  692. else
  693. {
  694. Status = SEC_E_ILLEGAL_MESSAGE;
  695. DebugLog((DEB_ERROR, "SpInitLsaModeContext: MaxBuf directive value too large 0x%x\n", Status));
  696. goto CleanUp;
  697. }
  698. }
  699. // We now have completed setup for the digest fields - time to process the data
  700. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Digest inputs processing completed\n"));
  701. ContextPrint(pContext);
  702. // Verify the directives utilized
  703. Status = DigestDirectiveCheck(&Digest, typeDigest);
  704. if (!NT_SUCCESS(Status))
  705. {
  706. DebugLog((DEB_ERROR, "SpInitLsaModeContext: DigestDirectiveCheck error 0x%x\n", Status));
  707. goto CleanUp;
  708. }
  709. Status = DigestGenerateParameters(pContext, &Digest, &ReplyBuffer);
  710. if (!NT_SUCCESS(Status))
  711. {
  712. DebugLog((DEB_ERROR, "SpInitLsaModeContext: DigestGenerateParameters error status 0x%x\n", Status));
  713. goto CleanUp;
  714. }
  715. // Now transfer the Challenge buffer to the ouput secbuffer
  716. if ((fContextReqFlags & ISC_REQ_ALLOCATE_MEMORY) == 0)
  717. {
  718. if (pOutputToken->cbBuffer < ReplyBuffer.cbBuffer)
  719. {
  720. DebugLog((DEB_ERROR,"SpInitLsaModeContext: Output token is too small - sent in %d, needed %d\n",
  721. pOutputToken->cbBuffer, ReplyBuffer.cbBuffer));
  722. pOutputToken->cbBuffer = ReplyBuffer.cbBuffer;
  723. Status = STATUS_BUFFER_TOO_SMALL;
  724. goto CleanUp;
  725. }
  726. RtlCopyMemory(pOutputToken->pvBuffer, ReplyBuffer.pvBuffer, ReplyBuffer.cbBuffer);
  727. pOutputToken->cbBuffer = ReplyBuffer.cbBuffer;
  728. }
  729. else
  730. {
  731. pOutputToken->pvBuffer = ReplyBuffer.pvBuffer;
  732. pOutputToken->cbBuffer = ReplyBuffer.cbBuffer;
  733. ReplyBuffer.pvBuffer = NULL;
  734. ReplyBuffer.cbBuffer = 0;
  735. fContextAttr |= ISC_RET_ALLOCATED_MEMORY;
  736. }
  737. pContext->ContextReq = fContextAttr;
  738. pContext->ExpirationTime = g_TimeForever; // never expire
  739. *fContextAttributes = pContext->ContextReq; // Return the ISC Attributes set on Context
  740. *NewContextHandle = (ULONG_PTR)pContext; // Just report back with the updated context
  741. if (pExpirationTime)
  742. {
  743. *pExpirationTime = pContext->ExpirationTime;
  744. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Context Expiration TimeStamp high/low 0x%x/0x%x\n",
  745. pExpirationTime->HighPart, pExpirationTime->LowPart));
  746. }
  747. // Check if need to check server's response auth
  748. if (pContext->ContextReq & ISC_RET_MUTUAL_AUTH)
  749. {
  750. // Calculate the expected response auth from the server
  751. Status = DigestCalculateResponseAuth(&Digest, &(pContext->strResponseAuth));
  752. if (!NT_SUCCESS(Status))
  753. {
  754. DebugLog((DEB_ERROR, "SpInitLsaModeContext, DigestCalculateResponseAuth Status 0x%x\n",
  755. Status));
  756. goto CleanUp;
  757. }
  758. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Pre-calculated RspAuth %Z\n",
  759. &(pContext->strResponseAuth)));
  760. // Keep copy of digest values for context map on last ISC call
  761. for (int iTemp = 0; iTemp < MD5_AUTH_LAST; iTemp++)
  762. {
  763. Status = StringDuplicate(&pContext->strDirective[iTemp], &(Digest.refstrParam[iTemp]));
  764. if (!NT_SUCCESS(Status))
  765. {
  766. DebugLog((DEB_ERROR, "SpInitLsaModeContext: Failed to copy directives Status 0x%x\n", Status));
  767. goto CleanUp;
  768. }
  769. }
  770. // Need to verify the output from final ASC call to verify server has session key
  771. Status = SEC_I_CONTINUE_NEEDED;
  772. }
  773. else
  774. {
  775. Status = SspMapDigestContext(pContext, &Digest, FLAG_CONTEXT_REFCOUNT, ContextData);
  776. if (!NT_SUCCESS(Status))
  777. {
  778. DebugLog((DEB_ERROR, "SpInitLsaModeContext: SspMapContext Status 0x%x\n", Status));
  779. goto CleanUp;
  780. }
  781. // this is last call, indicate to map the context
  782. *MappedContext = TRUE;
  783. }
  784. // Add the Newly created Context into the list of Contexts unless it was there before
  785. if (pNewContext)
  786. {
  787. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Added context 0x%x\n", pNewContext));
  788. pNewContext->lReferences = 1;
  789. CtxtHandlerInsertCred(pNewContext);
  790. // bLockedContext = TRUE; // Release memory to CtxtHandler
  791. pNewContext = NULL; // We no longer own this memory - turned over to CtxtHandler
  792. }
  793. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Will create UserContext on exit\n"));
  794. CleanUp:
  795. // Failed to complete operations if non-NULL so clean up
  796. if (pNewContext)
  797. {
  798. ContextFree(pNewContext);
  799. pNewContext = NULL;
  800. }
  801. // On Failure - clean up any allocated memory
  802. if (!NT_SUCCESS(Status))
  803. {
  804. // Clear up any mapped data
  805. *MappedContext = FALSE;
  806. if (ContextData->pvBuffer)
  807. {
  808. g_LsaFunctions->FreeLsaHeap(ContextData->pvBuffer);
  809. ContextData->pvBuffer = NULL;
  810. ContextData->cbBuffer = 0;
  811. }
  812. }
  813. // DeReference - pCredential
  814. if (pCredential)
  815. {
  816. SubStatus = CredHandlerRelease(pCredential);
  817. if (!NT_SUCCESS(SubStatus))
  818. {
  819. DebugLog((DEB_TRACE, "SpInitLsaModeContext: CredHandlerRelease error Status 0x%x\n", SubStatus));
  820. if (NT_SUCCESS(Status))
  821. {
  822. Status = SubStatus; // Indicate release error
  823. }
  824. }
  825. pCredential = NULL;
  826. }
  827. if (bLockedContext && pContext)
  828. { // happened only if ref counted a SecurityContext
  829. SubStatus = CtxtHandlerRelease(pContext, 1);
  830. if (!NT_SUCCESS(SubStatus))
  831. {
  832. DebugLog((DEB_TRACE, "SpInitLsaModeContext: CtxtHandlerRelease error Status 0x%x\n", SubStatus));
  833. if (NT_SUCCESS(Status))
  834. {
  835. Status = SubStatus; // Indicate release error
  836. }
  837. }
  838. pContext = NULL;
  839. }
  840. // Free up any allocated memory from the ouput reply buffer
  841. if (ReplyBuffer.pvBuffer)
  842. {
  843. DigestFreeMemory(ReplyBuffer.pvBuffer);
  844. ReplyBuffer.pvBuffer = NULL;
  845. ReplyBuffer.cbBuffer = 0;
  846. }
  847. // Clean up local memory used by Digest
  848. DigestFree(&Digest);
  849. StringFree(&strTargetName);
  850. DebugLog((DEB_TRACE, "SpInitLsaModeContext: Mapped context %d Flags IN:0x%lx OUT:0x%lx\n",
  851. *MappedContext, fContextReqFlags,*fContextAttributes));
  852. DebugLog((DEB_TRACE_FUNC, "SpInitLsaModeContext: Leaving Context 0x%x Status 0x%x\n", *NewContextHandle, Status));
  853. return(Status);
  854. }
  855. NTSTATUS NTAPI
  856. SpApplyControlToken(
  857. IN ULONG_PTR ContextHandle,
  858. IN PSecBufferDesc ControlToken
  859. )
  860. {
  861. DebugLog((DEB_TRACE_FUNC, "SpApplyControlToken: Entering/Leaving \n"));
  862. UNREFERENCED_PARAMETER(ContextHandle);
  863. UNREFERENCED_PARAMETER(ControlToken);
  864. return(SEC_E_UNSUPPORTED_FUNCTION);
  865. }
  866. //+-------------------------------------------------------------------------
  867. //
  868. // Function: SpAcceptLsaModeContext
  869. //
  870. // Synopsis: Digest implementation of AcceptSecurityContext call.
  871. //
  872. // Effects:
  873. //
  874. // Arguments:
  875. // CredentialHandle - Handle to the credentials to be used to
  876. // create the context.
  877. //
  878. // OldContextHandle - Handle to the partially formed context, if this is
  879. // a second call (see above) or NULL if this is the first call.
  880. //
  881. // InputToken - Pointer to the input token. In the first call this
  882. // token can either be NULL or may contain security package specific
  883. // information.
  884. //
  885. // ContextReqFlags - Requirements of the context, package specific.
  886. //
  887. // #define ASC_REQ_REPLAY_DETECT 0x00000004
  888. // #define ASC_REQ_SEQUENCE_DETECT 0x00000008
  889. // #define ASC_REQ_CONFIDENTIALITY 0x00000010
  890. // #define ASC_REQ_ALLOCATE_MEMORY 0x00000100
  891. //
  892. // TargetDataRep - Long indicating the data representation (byte ordering, etc)
  893. // on the target. The constant SECURITY_NATIVE_DREP may be supplied
  894. // by the transport indicating that the native format is in use.
  895. //
  896. // NewContextHandle - New context handle. If this is a second call, this
  897. // can be the same as OldContextHandle.
  898. //
  899. // OutputToken - Buffer to receive the output token.
  900. //
  901. // ContextAttributes -Attributes of the context established.
  902. //
  903. // #define ASC_RET_REPLAY_DETECT 0x00000004
  904. // #define ASC_RET_SEQUENCE_DETECT 0x00000008
  905. // #define ASC_RET_CONFIDENTIALITY 0x00000010
  906. // #define ASC_RET_ALLOCATED_BUFFERS 0x00000100
  907. //
  908. // ExpirationTime - Expiration time of the context.
  909. //
  910. //
  911. // Requires:
  912. //
  913. // Returns:
  914. // STATUS_SUCCESS - Message handled
  915. // SEC_I_CONTINUE_NEEDED -- Caller should call again later
  916. //
  917. // SEC_E_NO_SPM -- Security Support Provider is not running
  918. // SEC_E_INVALID_TOKEN -- Token improperly formatted
  919. // SEC_E_INVALID_HANDLE -- Credential/Context Handle is invalid
  920. // SEC_E_BUFFER_TOO_SMALL -- Buffer for output token isn't big enough
  921. // SEC_E_LOGON_DENIED -- User is no allowed to logon to this server
  922. // SEC_E_INSUFFICIENT_MEMORY -- Not enough memory
  923. //
  924. // Notes:
  925. //
  926. //--------------------------------------------------------------------------
  927. NTSTATUS NTAPI
  928. SpAcceptLsaModeContext(
  929. IN OPTIONAL ULONG_PTR CredentialHandle,
  930. IN OPTIONAL ULONG_PTR OldContextHandle,
  931. IN PSecBufferDesc InputBuffers,
  932. IN ULONG fContextReqFlags,
  933. IN ULONG TargetDataRep,
  934. OUT PULONG_PTR NewContextHandle,
  935. OUT PSecBufferDesc OutputBuffers,
  936. OUT PULONG fContextAttributes,
  937. OUT PTimeStamp pExpirationTime,
  938. OUT PBOOLEAN MappedContext,
  939. OUT PSecBuffer ContextData
  940. )
  941. {
  942. NTSTATUS Status = STATUS_SUCCESS;
  943. NTSTATUS SubStatus = STATUS_SUCCESS;
  944. NTSTATUS AuditLogStatus = STATUS_SUCCESS;
  945. NTSTATUS AuditLogSubStatus = STATUS_SUCCESS;
  946. DebugLog((DEB_TRACE_FUNC, "SpAcceptLsaModeContext: Entering ContextHandle 0x%x\n", OldContextHandle));
  947. SecBuffer TempTokens[6];
  948. PSecBuffer pChalRspInputToken;
  949. PSecBuffer pMethodInputToken;
  950. PSecBuffer pURIInputToken;
  951. PSecBuffer pHEntityInputToken;
  952. PSecBuffer pOutputToken;
  953. PSecBuffer pRealmInputToken;
  954. DIGEST_PARAMETER Digest;
  955. PDIGEST_CONTEXT pNewContext = NULL; // keep pointer to release new context on error
  956. BOOL bLockedCredential = FALSE;
  957. BOOL bLockedContext = FALSE;
  958. BOOL fLogonSessionCreated = FALSE;
  959. SecBuffer ReplyBuffer; // Output is generated in this buffer
  960. USHORT usLen = 0;
  961. char *cptr = NULL;
  962. ULONG fContextAttr = ASC_REQ_REPLAY_DETECT; // Flags on the Attributes of the context
  963. ULONG ulFlagOptions = 0;
  964. DIGEST_TYPE typeDigest = NO_DIGEST_SPECIFIED;
  965. QOP_TYPE typeQOP = NO_QOP_SPECIFIED;
  966. ALGORITHM_TYPE typeAlgorithm = NO_ALGORITHM_SPECIFIED;
  967. CHARSET_TYPE typeCharset = ISO_8859_1;
  968. DIGESTMODE_TYPE typeDigestMode = DIGESTMODE_UNDEFINED; // Are we in SASL or HTTP mode
  969. PDIGEST_CREDENTIAL pCredential = NULL;
  970. PDIGEST_CONTEXT pContext = NULL;
  971. STRING strcMethod = {0};
  972. STRING strcHEntity = {0};
  973. STRING strRealm = {0};
  974. UNICODE_STRING refustrRealm = {0};
  975. PUNICODE_STRING pustrAuditAccountName = NULL;
  976. PUNICODE_STRING pusrtAuditDomain = NULL;
  977. BOOL fDefChars = FALSE;
  978. BOOL fGenerateAudit = FALSE;
  979. DWORD dwInfoLen = 0;
  980. SECPKG_CALL_INFO CallInfo = {0};
  981. UNREFERENCED_PARAMETER(TargetDataRep);
  982. // Create pointers to tokens for processing
  983. pChalRspInputToken = &TempTokens[0];
  984. pMethodInputToken = &TempTokens[1];
  985. pURIInputToken = &TempTokens[2];
  986. pHEntityInputToken = &TempTokens[3];
  987. pRealmInputToken = &TempTokens[4];
  988. pOutputToken = &TempTokens[5];
  989. ZeroMemory(TempTokens,sizeof(TempTokens));
  990. ZeroMemory(&ReplyBuffer, sizeof(ReplyBuffer));
  991. Status = DigestInit(&Digest);
  992. if (!NT_SUCCESS(Status))
  993. {
  994. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Digest init error status 0x%x\n", Status));
  995. goto CleanUp;
  996. }
  997. // Initialize the output values
  998. if (!fContextAttributes || !NewContextHandle || !InputBuffers || !OutputBuffers)
  999. {
  1000. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Invalid arg (possible NULL pointer)\n"));
  1001. return STATUS_INVALID_PARAMETER;
  1002. }
  1003. *NewContextHandle = (ULONG_PTR)NULL;
  1004. *MappedContext = FALSE;
  1005. *fContextAttributes = 0;
  1006. if (pExpirationTime)
  1007. {
  1008. *pExpirationTime = g_TimeForever;
  1009. }
  1010. ContextData->pvBuffer = NULL;
  1011. ContextData->cbBuffer = 0;
  1012. if(g_LsaFunctions->GetCallInfo(&CallInfo))
  1013. {
  1014. // For this release - we do not support server side calls in kernel mode
  1015. if ((CallInfo.Attributes & SECPKG_CALL_KERNEL_MODE) != 0)
  1016. {
  1017. DebugLog((DEB_WARN, "SpAcceptLsaModeContext: Server side SSP functions not supported\n"));
  1018. return SEC_E_UNSUPPORTED_FUNCTION;
  1019. }
  1020. }
  1021. // Determine if we are in HTTP or SASL mode
  1022. // SASL mode has 1 or less buffers provided, HTTP has 5
  1023. if (InputBuffers->cBuffers > 1)
  1024. {
  1025. typeDigestMode = DIGESTMODE_HTTP;
  1026. }
  1027. else
  1028. {
  1029. typeDigestMode = DIGESTMODE_SASL;
  1030. }
  1031. // Must have a Credential Handle to perform processing
  1032. Status = CredHandlerHandleToPtr(CredentialHandle, FALSE, &pCredential);
  1033. if (!NT_SUCCESS(Status))
  1034. {
  1035. Status = SEC_E_UNKNOWN_CREDENTIALS;
  1036. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: No Credential Handle passed\n"));
  1037. goto CleanUp;
  1038. }
  1039. bLockedCredential = TRUE;
  1040. // Verify that credential is marked INBOUND for ASC call
  1041. if (!(pCredential->CredentialUseFlags & DIGEST_CRED_INBOUND))
  1042. {
  1043. Status = SEC_E_NOT_SUPPORTED;
  1044. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Credential not marked for Inbound connections\n"));
  1045. goto CleanUp;
  1046. }
  1047. // Retrieve the information from the SecBuffers & check proper formattting
  1048. // First check to make sure that that the proper number of buffers were passed
  1049. if (typeDigestMode == DIGESTMODE_HTTP)
  1050. {
  1051. // HTTP has 5 buffers in Input: ChallengeResponse, Method, URI, HEntity, Realm
  1052. if ((InputBuffers->cBuffers < ASC_HTTP_NUM_INPUT_BUFFERS) ||
  1053. (OutputBuffers->cBuffers < ASC_HTTP_NUM_OUTPUT_BUFFERS))
  1054. {
  1055. Status = SEC_E_INVALID_TOKEN;
  1056. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Incorrect number of Input/Output HTTP Sec Buffers\n"));
  1057. goto CleanUp;
  1058. }
  1059. }
  1060. else
  1061. {
  1062. // SASL has 1 buffer in Input: ChallengeResponse
  1063. if ((InputBuffers->cBuffers < ASC_SASL_NUM_INPUT_BUFFERS) ||
  1064. (OutputBuffers->cBuffers < ASC_SASL_NUM_OUTPUT_BUFFERS))
  1065. {
  1066. Status = SEC_E_INVALID_TOKEN;
  1067. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Incorrect number of Input/Ouput SASL Sec Buffers\n"));
  1068. goto CleanUp;
  1069. }
  1070. fContextAttr |= ASC_RET_MUTUAL_AUTH; // SASL requires response auth from server
  1071. }
  1072. if ( !SspGetTokenBufferByIndex( InputBuffers,
  1073. 0, // get the first SECBUFFER_TOKEN
  1074. &pChalRspInputToken,
  1075. TRUE ) ||
  1076. !ContextIsTokenOK(pChalRspInputToken,NTDIGEST_SP_MAX_TOKEN_SIZE))
  1077. {
  1078. Status = SEC_E_INVALID_TOKEN;
  1079. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: SspGetTokenBufferByIndex (ChalRspInputToken) returns 0x%x\n", Status));
  1080. goto CleanUp;
  1081. }
  1082. if ( !SspGetTokenBufferByIndex( OutputBuffers,
  1083. 0, // get the first SECBUFFER_TOKEN
  1084. &pOutputToken,
  1085. FALSE ) ||
  1086. !ContextIsTokenOK(pOutputToken, 0))
  1087. {
  1088. Status = SEC_E_INVALID_TOKEN;
  1089. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext, SspGetTokenBufferByIndex (OutputToken) returns 0x%x\n", Status));
  1090. goto CleanUp;
  1091. }
  1092. if (fContextReqFlags & ASC_REQ_ALLOCATE_MEMORY)
  1093. {
  1094. pOutputToken->pvBuffer = NULL;
  1095. pOutputToken->cbBuffer = 0;
  1096. }
  1097. // Reset output buffer if provided
  1098. if ((pOutputToken->pvBuffer) && (pOutputToken->cbBuffer >= 1))
  1099. {
  1100. cptr = (char *)pOutputToken->pvBuffer;
  1101. *cptr = '\0';
  1102. }
  1103. //
  1104. // If no ChallengeResponse data provided (only NULL in buffer), then this is the first call
  1105. // Determine a nonce, open up a null context, and return it. Return SEC_E_INCOMPLETE_MESSAGE to
  1106. // indicate that a challenge-response is expected
  1107. //
  1108. if ((!pChalRspInputToken->pvBuffer) || (pChalRspInputToken->cbBuffer <= 1))
  1109. {
  1110. pNewContext = (PDIGEST_CONTEXT)DigestAllocateMemory(sizeof(DIGEST_CONTEXT));
  1111. if (!pNewContext)
  1112. {
  1113. Status = SEC_E_INSUFFICIENT_MEMORY;
  1114. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: out of memory on challenge context\n"));
  1115. goto CleanUp;
  1116. }
  1117. Status = ContextInit(pNewContext, pCredential);
  1118. if (!NT_SUCCESS (Status))
  1119. {
  1120. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: ContextInit error 0x%x\n", Status));
  1121. goto CleanUp;
  1122. }
  1123. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Context created 0x%x\n", pNewContext));
  1124. if (typeDigestMode == DIGESTMODE_HTTP)
  1125. {
  1126. typeDigest = DIGEST_SERVER;
  1127. }
  1128. else
  1129. {
  1130. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: SASL Requested\n"));
  1131. typeDigest = SASL_SERVER;
  1132. }
  1133. pNewContext->typeDigest = typeDigest;
  1134. // Determine which character set to utilize
  1135. if (((typeDigest == SASL_SERVER) && (g_fParameter_UTF8SASL == TRUE)) ||
  1136. ((typeDigest == DIGEST_SERVER) && (g_fParameter_UTF8HTTP == TRUE)))
  1137. {
  1138. typeCharset = UTF_8;
  1139. }
  1140. else
  1141. {
  1142. typeCharset = ISO_8859_1;
  1143. }
  1144. pNewContext->typeCharset = typeCharset;
  1145. // We will use the Opaque as the CNonce
  1146. Status = OpaqueCreate(&(pNewContext->strOpaque));
  1147. if (!NT_SUCCESS (Status))
  1148. {
  1149. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: OpaqueCreate error 0x%x\n", Status));
  1150. goto CleanUp;
  1151. }
  1152. Status = NonceCreate(&(pNewContext->strNonce));
  1153. if (!NT_SUCCESS (Status))
  1154. {
  1155. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: NonceCreate error 0x%x\n", Status));
  1156. goto CleanUp;
  1157. }
  1158. if (pNewContext->typeDigest == DIGEST_SERVER)
  1159. {
  1160. // Now see if a Realm was passed in to use for this challenge - the value could be single byte or Unicode
  1161. // Order is if realm passed to ASC use that, else just use the current domain name
  1162. if ( !SspGetTokenBufferByIndex( InputBuffers,
  1163. 4, // get the fifth SECBUFFER_TOKEN
  1164. &pRealmInputToken,
  1165. TRUE ) ||
  1166. !ContextIsTokenOK(pRealmInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE))
  1167. {
  1168. Status = SEC_E_INVALID_TOKEN;
  1169. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: SspGetTokenBufferByIndex (RealmInputToken) returns 0x%x\n", Status));
  1170. goto CleanUp;
  1171. }
  1172. if (pRealmInputToken->pvBuffer)
  1173. {
  1174. if (PBUFFERTYPE(pRealmInputToken) != SECBUFFER_PKG_PARAMS)
  1175. {
  1176. Status = SEC_E_INVALID_TOKEN;
  1177. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Realm buffer type invalid error 0x%x\n", Status));
  1178. goto CleanUp;
  1179. }
  1180. if (pRealmInputToken->cbBuffer)
  1181. {
  1182. usLen = ustrlencounted((const short *)pRealmInputToken->pvBuffer, (USHORT)pRealmInputToken->cbBuffer);
  1183. if (usLen > 0)
  1184. {
  1185. refustrRealm.Length = usLen * sizeof(WCHAR);
  1186. refustrRealm.MaximumLength = (unsigned short)(pRealmInputToken->cbBuffer);
  1187. refustrRealm.Buffer = (PWSTR)pRealmInputToken->pvBuffer; // refernce memory - no alloc!!!!
  1188. // For compat with RFC, the Realm must be encoded in ISO 8859-1
  1189. Status = EncodeUnicodeString(&refustrRealm, CP_8859_1, &strRealm, &fDefChars);
  1190. if (!NT_SUCCESS(Status))
  1191. {
  1192. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Input Realm ISO 8859-1 encoding error\n"));
  1193. goto CleanUp;
  1194. }
  1195. if (fDefChars == TRUE)
  1196. {
  1197. // We could not encode the provided Realm within ISO 8859-1 characters
  1198. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Input Realm can not be encoded in ISO 8859-1\n"));
  1199. Status = STATUS_UNMAPPABLE_CHARACTER;
  1200. goto CleanUp;
  1201. }
  1202. }
  1203. }
  1204. else
  1205. {
  1206. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: a blank realm is requested\n"));
  1207. Status = StringAllocate(&strRealm, 1);
  1208. if (!NT_SUCCESS(Status))
  1209. {
  1210. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: setting blank realm error\n"));
  1211. goto CleanUp;
  1212. }
  1213. strRealm.Length = 0; // indicate blank realm
  1214. }
  1215. }
  1216. }
  1217. typeAlgorithm = MD5_SESS;
  1218. pNewContext->typeAlgorithm = typeAlgorithm;
  1219. // Determine if we can process the QOP specified
  1220. if (fContextReqFlags & ASC_REQ_CONFIDENTIALITY)
  1221. {
  1222. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: AUTH-CONF requested\n"));
  1223. fContextAttr |= (ASC_RET_CONFIDENTIALITY | ASC_REQ_INTEGRITY);
  1224. typeQOP = AUTH_CONF; // Offer AUTH-CONF, AUTH_INT, and AUTH
  1225. }
  1226. else if (fContextReqFlags & ASC_REQ_INTEGRITY)
  1227. {
  1228. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: AUTH-INT requested\n"));
  1229. fContextAttr |= ASC_RET_INTEGRITY;
  1230. typeQOP = AUTH_INT; // Offer AUTH-INT and AUTH
  1231. }
  1232. else
  1233. typeQOP = AUTH; // Offer AUTH
  1234. pNewContext->typeQOP = typeQOP;
  1235. // Establish the attribute flags for this security context
  1236. pNewContext->ContextReq = fContextAttr;
  1237. Status = ContextCreateChal(pNewContext, &strRealm, &ReplyBuffer);
  1238. if (!NT_SUCCESS (Status))
  1239. {
  1240. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Failed to create Challenge status 0x%x\n", Status));
  1241. goto CleanUp;
  1242. }
  1243. // Now transfer the Challenge buffer to the ouput secbuffer
  1244. if ((fContextReqFlags & ASC_REQ_ALLOCATE_MEMORY) == 0)
  1245. {
  1246. if (pOutputToken->cbBuffer < ReplyBuffer.cbBuffer)
  1247. {
  1248. DebugLog((DEB_ERROR,"SpAcceptLsaModeContext:Output token is too small - sent in %d, needed %d\n",
  1249. pOutputToken->cbBuffer, ReplyBuffer.cbBuffer));
  1250. pOutputToken->cbBuffer = ReplyBuffer.cbBuffer;
  1251. Status = STATUS_BUFFER_TOO_SMALL;
  1252. goto CleanUp;
  1253. }
  1254. RtlCopyMemory(pOutputToken->pvBuffer, ReplyBuffer.pvBuffer, ReplyBuffer.cbBuffer);
  1255. pOutputToken->cbBuffer = ReplyBuffer.cbBuffer;
  1256. }
  1257. else
  1258. {
  1259. pOutputToken->pvBuffer = ReplyBuffer.pvBuffer;
  1260. pOutputToken->cbBuffer = ReplyBuffer.cbBuffer;
  1261. ReplyBuffer.pvBuffer = NULL;
  1262. ReplyBuffer.cbBuffer = 0;
  1263. fContextAttr |= ASC_RET_ALLOCATED_MEMORY;
  1264. }
  1265. // Update any new attributes
  1266. pNewContext->ContextReq = fContextAttr;
  1267. // Set the time expiration for this context
  1268. // This time is in 100 Nanoseconds since 1604
  1269. pNewContext->ExpirationTime = g_TimeForever; // Need to implement stale=true if enable
  1270. #if 0
  1271. Status = NtQuerySystemTime (&liContextTime);
  1272. if (!NT_SUCCESS(Status))
  1273. {
  1274. Status = STATUS_INTERNAL_ERROR;
  1275. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Failed to get current time\n"));
  1276. goto CleanUp;
  1277. }
  1278. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Current TimeStamp high/low 0x%x/0x%x\n",
  1279. liContextTime.HighPart, liContextTime.LowPart));
  1280. PrintTimeString(liContextTime, TRUE);
  1281. // g_dwParameter_Lifetime is in number of seconds - convert to number of 100 nanoseconds
  1282. liContextTime.QuadPart += ((LONGLONG)g_dwParameter_Lifetime * (LONGLONG)SECONDS_TO_100NANO);
  1283. if (pExpirationTime)
  1284. {
  1285. *pExpirationTime = liContextTime;
  1286. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Context Expiration TimeStamp high/low 0x%x/0x%x\n",
  1287. pExpirationTime->HighPart, pExpirationTime->LowPart));
  1288. PrintTimeString(liContextTime, TRUE);
  1289. }
  1290. pNewContext->ExpirationTime = liContextTime;
  1291. #endif
  1292. pNewContext->lReferences = 1;
  1293. // Map the partially formed context over to application space to support re-connect for ref counts on usercontext
  1294. Status = SspMapDigestContext(pNewContext, &Digest,
  1295. (FLAG_CONTEXT_REFCOUNT | FLAG_CONTEXT_PARTIAL | FLAG_CONTEXT_SERVER), ContextData);
  1296. if (!NT_SUCCESS(Status))
  1297. {
  1298. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext, SspMapContext returns %d\n", Status));
  1299. goto CleanUp;
  1300. }
  1301. *MappedContext = TRUE;
  1302. // Add it into the list of Contexts
  1303. CtxtHandlerInsertCred(pNewContext);
  1304. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Context added to list Opaque = %s\n", (pNewContext->strOpaque).Buffer));
  1305. *NewContextHandle = (ULONG_PTR)pNewContext;
  1306. *fContextAttributes = fContextAttr; // Return the ASC Attributes set on Context
  1307. pNewContext = NULL; // We no longer own this memory - turned over to CtxtHandler
  1308. Status = SEC_I_CONTINUE_NEEDED;
  1309. goto CleanUp;
  1310. }
  1311. // Processing ChallengeResponse (challenge was handled right before this
  1312. // We have input in the SECBUFFER 0th location - parse it
  1313. Status = DigestParser2(pChalRspInputToken, MD5_AUTH_NAMES, MD5_AUTH_LAST, &Digest);
  1314. if (!NT_SUCCESS(Status))
  1315. {
  1316. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: DigestParser error 0x%x\n", Status));
  1317. goto CleanUp;
  1318. }
  1319. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: DigestParser Returned OK\n"));
  1320. // HTTP has special Buffer needs in that it must pass in the METHOD, HEntity
  1321. if (typeDigestMode == DIGESTMODE_HTTP)
  1322. {
  1323. if ( !SspGetTokenBufferByIndex( InputBuffers,
  1324. 1, // get the second SECBUFFER_TOKEN
  1325. &pMethodInputToken,
  1326. TRUE ) ||
  1327. !ContextIsTokenOK(pMethodInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE))
  1328. { // Check to make sure that string is present
  1329. Status = SEC_E_INVALID_TOKEN;
  1330. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: SspGetTokenBufferByIndex (MethodInputToken) returns 0x%x\n", Status));
  1331. goto CleanUp;
  1332. }
  1333. /* // Not used in this version, may be used in the future
  1334. if ( !SspGetTokenBufferByIndex( InputBuffers,
  1335. 2, // get the third SECBUFFER_TOKEN
  1336. &pURIInputToken,
  1337. TRUE ) ||
  1338. !ContextIsTokenOK(pURIInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE))
  1339. {
  1340. Status = SEC_E_INVALID_TOKEN;
  1341. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: SspGetTokenBufferByIndex (URIInputToken) returns %d\n", Status));
  1342. goto CleanUp;
  1343. }
  1344. */
  1345. if ( !SspGetTokenBufferByIndex( InputBuffers,
  1346. 3, // get the forth SECBUFFER_TOKEN
  1347. &pHEntityInputToken,
  1348. TRUE ) ||
  1349. !ContextIsTokenOK(pHEntityInputToken, NTDIGEST_SP_MAX_TOKEN_SIZE))
  1350. {
  1351. Status = SEC_E_INVALID_TOKEN;
  1352. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: SspGetTokenBufferByIndex (HEntityInputToken) returns 0x%x\n", Status));
  1353. goto CleanUp;
  1354. }
  1355. // Verify that there is a valid Method provided
  1356. if (!pMethodInputToken->pvBuffer || !pMethodInputToken->cbBuffer ||
  1357. (PBUFFERTYPE(pMethodInputToken) != SECBUFFER_PKG_PARAMS))
  1358. {
  1359. Status = SEC_E_INVALID_TOKEN;
  1360. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Method SecBuffer must have valid method string status 0x%x\n", Status));
  1361. goto CleanUp;
  1362. }
  1363. usLen = strlencounted((char *)pMethodInputToken->pvBuffer, (USHORT)pMethodInputToken->cbBuffer);
  1364. if (!usLen)
  1365. {
  1366. Status = SEC_E_INVALID_TOKEN;
  1367. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Method SecBuffer must have valid method string status 0x%x\n", Status));
  1368. goto CleanUp;
  1369. }
  1370. Digest.refstrParam[MD5_AUTH_METHOD].Length = usLen;
  1371. Digest.refstrParam[MD5_AUTH_METHOD].MaximumLength = (unsigned short)(pMethodInputToken->cbBuffer);
  1372. Digest.refstrParam[MD5_AUTH_METHOD].Buffer = (char *)pMethodInputToken->pvBuffer; // refernce memory - no alloc!!!!
  1373. // Check to see if we have H(Entity) data to utilize
  1374. if (pHEntityInputToken->cbBuffer)
  1375. {
  1376. // Verify that there is a valid Method provided
  1377. if (!pHEntityInputToken->pvBuffer || (PBUFFERTYPE(pMethodInputToken) != SECBUFFER_PKG_PARAMS))
  1378. {
  1379. Status = SEC_E_INVALID_TOKEN;
  1380. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: HEntity SecBuffer must have valid string status 0x%x\n", Status));
  1381. goto CleanUp;
  1382. }
  1383. usLen = strlencounted((char *)pHEntityInputToken->pvBuffer, (USHORT)pHEntityInputToken->cbBuffer);
  1384. if ((usLen != 0) && (usLen != (MD5_HASH_BYTESIZE * 2)))
  1385. {
  1386. Status = SEC_E_INVALID_TOKEN;
  1387. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: HEntity SecBuffer must have valid MD5 Hash data 0x%x\n", Status));
  1388. goto CleanUp;
  1389. }
  1390. if (usLen)
  1391. {
  1392. Digest.refstrParam[MD5_AUTH_HENTITY].Length = usLen;
  1393. Digest.refstrParam[MD5_AUTH_HENTITY].MaximumLength = (unsigned short)(pHEntityInputToken->cbBuffer);
  1394. Digest.refstrParam[MD5_AUTH_HENTITY].Buffer = (char *)pHEntityInputToken->pvBuffer; // refernce memory - no alloc!!!!
  1395. }
  1396. }
  1397. typeDigest = DIGEST_SERVER;
  1398. // Determine the type of algorithm to utilize - Digest servers will not respond to MD5 requests
  1399. // this is a security concern for cluster environments since that H(A1) returned can be used in MD5_sesss requests
  1400. // Determine which Algorithm to support under HTTP
  1401. Status = CheckItemInList(MD5_SESSSTR, &(Digest.refstrParam[MD5_AUTH_ALGORITHM]), FALSE);
  1402. if (!NT_SUCCESS(Status))
  1403. {
  1404. // Check if MD5 specified (or none specified so MD5 defaults)
  1405. Status = CheckItemInList(MD5STR, &(Digest.refstrParam[MD5_AUTH_ALGORITHM]), FALSE);
  1406. if (NT_SUCCESS(Status) || (Digest.refstrParam[MD5_AUTH_ALGORITHM].Length == 0))
  1407. {
  1408. typeAlgorithm = MD5;
  1409. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Client requested MD5 (or defaulted); selected as algorithm\n"));
  1410. Status = SEC_E_QOP_NOT_SUPPORTED;
  1411. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Server does not support MD5 - allows only MD5-sess\n"));
  1412. goto CleanUp;
  1413. }
  1414. else
  1415. {
  1416. Status = SEC_E_QOP_NOT_SUPPORTED;
  1417. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Unknown Server algorithms provided\n"));
  1418. goto CleanUp;
  1419. }
  1420. }
  1421. else
  1422. {
  1423. typeAlgorithm = MD5_SESS;
  1424. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Client requested MD5-sess; selected as algorithm\n"));
  1425. }
  1426. }
  1427. else
  1428. {
  1429. // All others follow the SASL Interface so there are default values
  1430. typeDigest = SASL_SERVER;
  1431. // SASL only supports MD5_SESS - client will not send
  1432. typeAlgorithm = MD5_SESS;
  1433. // Set Method to Authenticate
  1434. RtlInitString(&strcMethod, AUTHENTICATESTR);
  1435. StringReference(&(Digest.refstrParam[MD5_AUTH_METHOD]), &strcMethod); // refernce memory - no alloc!!!!
  1436. RtlInitString(&strcHEntity, ZERO32STR);
  1437. StringReference(&(Digest.refstrParam[MD5_AUTH_HENTITY]), &strcHEntity); // refernce memory - no alloc!!!!
  1438. }
  1439. fContextAttr |= (ASC_RET_REPLAY_DETECT | ASC_RET_SEQUENCE_DETECT);
  1440. if (NT_SUCCESS(CheckItemInList(AUTHCONFSTR, &(Digest.refstrParam[MD5_AUTH_QOP]), TRUE)))
  1441. {
  1442. // client requested AUTH-CONF since privacy requested
  1443. fContextAttr |= (ASC_RET_CONFIDENTIALITY | ASC_RET_INTEGRITY);
  1444. typeQOP = AUTH_CONF;
  1445. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Client selected QOP=AUTH-CONF\n"));
  1446. }
  1447. else if (NT_SUCCESS(CheckItemInList(AUTHINTSTR, &(Digest.refstrParam[MD5_AUTH_QOP]), TRUE)))
  1448. {
  1449. // client requested AUTH-INT since privacy requested
  1450. fContextAttr |= ASC_RET_INTEGRITY;
  1451. typeQOP = AUTH_INT;
  1452. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Client selected QOP=AUTH-INT\n"));
  1453. }
  1454. else if (NT_SUCCESS(CheckItemInList(AUTHSTR, &(Digest.refstrParam[MD5_AUTH_QOP]), TRUE)))
  1455. {
  1456. // check to see if client specified auth only
  1457. typeQOP = AUTH;
  1458. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Client selected QOP=AUTH\n"));
  1459. }
  1460. else
  1461. { // Client did not specify any QOP
  1462. if (!Digest.refstrParam[MD5_AUTH_QOP].Length)
  1463. {
  1464. if (typeDigestMode == DIGESTMODE_HTTP)
  1465. {
  1466. typeQOP = NO_QOP_SPECIFIED; // This is OK - acts like AUTH but response different
  1467. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Client did not specify QOP (HTTP only)\n"));
  1468. }
  1469. else
  1470. {
  1471. typeQOP = AUTH; // This is OK - SASL defaults to AUTH
  1472. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Client did not specify QOP, default to AUTH\n"));
  1473. }
  1474. }
  1475. else
  1476. {
  1477. // Failed to provide recognized QOP
  1478. Status = SEC_E_QOP_NOT_SUPPORTED;
  1479. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Client failed to provide recognized QOP\n"));
  1480. goto CleanUp;
  1481. }
  1482. }
  1483. // If there is no OldContextToken passed in, then check for SecurityContext handle (in opaque) else return error
  1484. if ( !ARGUMENT_PRESENT( OldContextHandle ))
  1485. {
  1486. // Search for Reference to SecurityContextHandle
  1487. Status = CtxtHandlerOpaqueToPtr(&(Digest.refstrParam[MD5_AUTH_OPAQUE]), &pContext);
  1488. if (!NT_SUCCESS (Status))
  1489. {
  1490. Status = SEC_E_INVALID_TOKEN;
  1491. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: CtxtHandlerOpaqueToPtr error 0x%x\n", Status));
  1492. goto CleanUp;
  1493. }
  1494. }
  1495. else
  1496. {
  1497. // We have a SecurityContextHandle to use - see if it is in the ContextList and valid
  1498. Status = CtxtHandlerHandleToContext(OldContextHandle, FALSE, &pContext);
  1499. if (!NT_SUCCESS (Status))
  1500. {
  1501. Status = SEC_E_INVALID_TOKEN;
  1502. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: CtxtHandlerHandleToContext error 0x%x\n", Status));
  1503. goto CleanUp;
  1504. }
  1505. }
  1506. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Acquired Context ptr for 0x%x\n", pContext));
  1507. bLockedContext = TRUE;
  1508. // Can only call AcceptSecurityContect Once after ChallengeResponse
  1509. // For non-persistent connections (no OldContextHandle passed in), just return SCH and return
  1510. if (pContext->strSessionKey.Length)
  1511. {
  1512. if (ARGUMENT_PRESENT( OldContextHandle ))
  1513. {
  1514. Status = STATUS_LOGON_FAILURE;
  1515. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Multiple call to completed ASC\n"));
  1516. goto CleanUp;
  1517. }
  1518. else
  1519. {
  1520. // Map over a context, but really only used to ref count the userSecurityContext handle count
  1521. Status = SspMapDigestContext(pContext, &Digest, (FLAG_CONTEXT_REFCOUNT | FLAG_CONTEXT_SERVER), ContextData);
  1522. if (!NT_SUCCESS(Status))
  1523. {
  1524. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext, SspMapContext returns %d\n", Status));
  1525. goto CleanUp;
  1526. }
  1527. *MappedContext = TRUE;
  1528. Status = SEC_I_COMPLETE_NEEDED;
  1529. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Opaque located CtxtHandle, return handle, Complete needed\n"));
  1530. *NewContextHandle = (ULONG_PTR)pContext; // Just report back with the updated context
  1531. pContext = NULL;
  1532. goto CleanUp;
  1533. }
  1534. }
  1535. // Check to see if the Server has provided character set for encoding - only UTF-8 accepted
  1536. Status = CheckItemInList(MD5_UTF8STR, &(Digest.refstrParam[MD5_AUTH_CHARSET]), TRUE);
  1537. if (NT_SUCCESS(Status))
  1538. {
  1539. // The ChallengeResponse requested UTF-8 encoding, check to see that server allowed this
  1540. if (((typeDigest == SASL_SERVER) && (g_fParameter_UTF8SASL == TRUE)) ||
  1541. ((typeDigest == DIGEST_SERVER) && (g_fParameter_UTF8HTTP == TRUE)))
  1542. {
  1543. typeCharset = UTF_8;
  1544. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Server allows UTF-8 encoding\n"));
  1545. }
  1546. else
  1547. {
  1548. // We did not authorize this type of encoding - fail the request
  1549. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Client requested UTF-8 server did not offer\n"));
  1550. Status = SEC_E_ILLEGAL_MESSAGE;
  1551. goto CleanUp;
  1552. }
  1553. }
  1554. // We now have a pointer to the Security Context to use, finish up setting up the digestparamter fields
  1555. // Set the type of Digest Parameters we are to process
  1556. pContext->typeDigest = typeDigest;
  1557. pContext->typeAlgorithm = typeAlgorithm;
  1558. pContext->typeQOP = typeQOP;
  1559. pContext->typeCharset = typeCharset;
  1560. if (pContext->typeQOP == AUTH_CONF)
  1561. {
  1562. Status = CheckItemInList(STR_CIPHER_RC4, &(Digest.refstrParam[MD5_AUTH_CIPHER]), TRUE);
  1563. if (NT_SUCCESS(Status))
  1564. {
  1565. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Selecting RC4 for auth-conf cipher\n"));
  1566. pContext->typeCipher = CIPHER_RC4;
  1567. }
  1568. else
  1569. {
  1570. Status = CheckItemInList(STR_CIPHER_3DES, &(Digest.refstrParam[MD5_AUTH_CIPHER]), TRUE);
  1571. if (NT_SUCCESS(Status))
  1572. {
  1573. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Selecting Triple DES for auth-conf cipher\n"));
  1574. pContext->typeCipher = CIPHER_3DES;
  1575. }
  1576. else
  1577. {
  1578. Status = CheckItemInList(STR_CIPHER_RC4_56, &(Digest.refstrParam[MD5_AUTH_CIPHER]), TRUE);
  1579. if (NT_SUCCESS(Status))
  1580. {
  1581. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Selecting RC4-56 for auth-conf cipher\n"));
  1582. pContext->typeCipher = CIPHER_RC4_56;
  1583. }
  1584. else
  1585. {
  1586. Status = CheckItemInList(STR_CIPHER_RC4_40, &(Digest.refstrParam[MD5_AUTH_CIPHER]), TRUE);
  1587. if (NT_SUCCESS(Status))
  1588. {
  1589. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Selecting RC4-40 for auth-conf cipher\n"));
  1590. pContext->typeCipher = CIPHER_RC4_40;
  1591. }
  1592. else
  1593. {
  1594. Status = CheckItemInList(STR_CIPHER_DES, &(Digest.refstrParam[MD5_AUTH_CIPHER]), TRUE);
  1595. if (NT_SUCCESS(Status))
  1596. {
  1597. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Selecting DES for auth-conf cipher\n"));
  1598. pContext->typeCipher = CIPHER_DES;
  1599. }
  1600. else
  1601. {
  1602. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Failed to find known ciper selected by client\n"));
  1603. Status = STATUS_CRYPTO_SYSTEM_INVALID;
  1604. goto CleanUp;
  1605. }
  1606. }
  1607. }
  1608. }
  1609. }
  1610. }
  1611. // Check if client specified a MaxBuffer
  1612. if (Digest.refstrParam[MD5_AUTH_MAXBUF].Length && Digest.refstrParam[MD5_AUTH_MAXBUF].Buffer)
  1613. {
  1614. if (Digest.refstrParam[MD5_AUTH_MAXBUF].Length < MAXBUFNUMLEN)
  1615. {
  1616. ULONG ulMaxBuf = 0;
  1617. CHAR czMaxBuf[MAXBUFNUMLEN + 1];
  1618. ZeroMemory(czMaxBuf, (MAXBUFNUMLEN + 1));
  1619. memcpy(czMaxBuf, Digest.refstrParam[MD5_AUTH_MAXBUF].Buffer, Digest.refstrParam[MD5_AUTH_MAXBUF].Length);
  1620. Status = RtlCharToInteger(czMaxBuf, TENBASE, &ulMaxBuf);
  1621. if (!NT_SUCCESS(Status))
  1622. {
  1623. Status = SEC_E_ILLEGAL_MESSAGE;
  1624. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: MaxBuf directive value malformed 0x%x\n", Status));
  1625. goto CleanUp;
  1626. }
  1627. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Client MaxBuf set to %lu\n", ulMaxBuf));
  1628. pContext->ulSendMaxBuf = ulMaxBuf;
  1629. }
  1630. else
  1631. {
  1632. Status = SEC_E_ILLEGAL_MESSAGE;
  1633. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: MaxBuf directive value too large 0x%x\n", Status));
  1634. goto CleanUp;
  1635. }
  1636. }
  1637. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Digest inputs processing completed\n"));
  1638. // Verify the directives utilized
  1639. Status = DigestDirectiveCheck(&Digest, typeDigest);
  1640. if (!NT_SUCCESS(Status))
  1641. {
  1642. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: DigestDirectiveCheck error 0x%x\n", Status));
  1643. goto CleanUp;
  1644. }
  1645. // ReplyBuffer will contain the ResponseAuth if generated
  1646. Status = DigestProcessParameters(pContext, &Digest, &ReplyBuffer,
  1647. &AuditLogStatus, &AuditLogSubStatus, &fGenerateAudit);
  1648. if (!NT_SUCCESS(Status))
  1649. {
  1650. // Checking for broken clients that do not do the backslash encoding properly
  1651. if ((g_dwParameter_ServerCompat & SERVERCOMPAT_BS_ENCODE) &&
  1652. (typeDigestMode == DIGESTMODE_HTTP) &&
  1653. (Digest.usFlags & FLAG_BS_ENCODE_CLIENT_BROKEN))
  1654. {
  1655. NTSTATUS RetryStatus = STATUS_SUCCESS;
  1656. NTSTATUS AuditLogRetryStatus = STATUS_SUCCESS;
  1657. NTSTATUS AuditLogSubRetryStatus = STATUS_SUCCESS;
  1658. BOOL fGenerateAuditRetry = FALSE;
  1659. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: Retry with no BS decode\n"));
  1660. Digest.usFlags |= FLAG_NOBS_DECODE;
  1661. // Processing ChallengeResponse without doing a backslash decode on directive values
  1662. Status = DigestParser2(pChalRspInputToken, MD5_AUTH_NAMES, MD5_AUTH_LAST, &Digest);
  1663. if (!NT_SUCCESS(Status))
  1664. {
  1665. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: DigestParser error 0x%x\n", Status));
  1666. goto CleanUp;
  1667. }
  1668. // ReplyBuffer will contain the ResponseAuth if generated
  1669. RetryStatus = DigestProcessParameters(pContext, &Digest, &ReplyBuffer,
  1670. &AuditLogRetryStatus, &AuditLogSubRetryStatus, &fGenerateAuditRetry);
  1671. if (!NT_SUCCESS(RetryStatus))
  1672. {
  1673. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: DigestProcessParameters retry error 0x%x\n", Status));
  1674. goto CleanUp;
  1675. }
  1676. AuditLogStatus = AuditLogRetryStatus;
  1677. AuditLogSubStatus = AuditLogSubRetryStatus;
  1678. fGenerateAudit = fGenerateAuditRetry;
  1679. // Indicate that Backslash Encoded Broken client worked - future decode use no backslash encoding
  1680. pContext->ulFlags |= FLAG_CONTEXT_NOBS_DECODE;
  1681. }
  1682. else
  1683. {
  1684. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: DigestProcessParameters error 0x%x\n", Status));
  1685. goto CleanUp;
  1686. }
  1687. }
  1688. fLogonSessionCreated = TRUE; // We have successfully authed the request & created LogonID & Token
  1689. if ((fContextReqFlags & ASC_REQ_ALLOCATE_MEMORY) == 0)
  1690. {
  1691. if (pOutputToken->cbBuffer < ReplyBuffer.cbBuffer)
  1692. {
  1693. DebugLog((DEB_ERROR,"SpAcceptLsaModeContext:Output token is too small - sent in %d, needed %d\n",
  1694. pOutputToken->cbBuffer, ReplyBuffer.cbBuffer));
  1695. pOutputToken->cbBuffer = ReplyBuffer.cbBuffer;
  1696. Status = STATUS_BUFFER_TOO_SMALL;
  1697. goto CleanUp;
  1698. }
  1699. RtlCopyMemory(pOutputToken->pvBuffer, ReplyBuffer.pvBuffer, ReplyBuffer.cbBuffer);
  1700. pOutputToken->cbBuffer = ReplyBuffer.cbBuffer;
  1701. }
  1702. else
  1703. {
  1704. pOutputToken->pvBuffer = ReplyBuffer.pvBuffer;
  1705. pOutputToken->cbBuffer = ReplyBuffer.cbBuffer;
  1706. ReplyBuffer.pvBuffer = NULL;
  1707. ReplyBuffer.cbBuffer = 0;
  1708. fContextAttr |= ASC_RET_ALLOCATED_MEMORY;
  1709. }
  1710. // Establish the attribute flags for this security context
  1711. pContext->ContextReq = fContextAttr;
  1712. // Keep a copy of the Cnonce for future Delegation requests
  1713. Status = StringDuplicate(&pContext->strCNonce, &Digest.refstrParam[MD5_AUTH_CNONCE]);
  1714. if (!NT_SUCCESS (Status))
  1715. {
  1716. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: StringDuplicate CNonce failed status 0x%x\n", Status));
  1717. goto CleanUp;
  1718. }
  1719. // Now create a LogonSession for the completed LogonToken contained SecurityContext
  1720. // This can be utilized in delegated digest client's ACH
  1721. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Adding a LogonSession for successful ASC\n"));
  1722. Status = CtxtCreateLogSess(pContext);
  1723. if (!NT_SUCCESS (Status))
  1724. {
  1725. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: CtxtCreateLogSess failed status 0x%x\n", Status));
  1726. goto CleanUp;
  1727. }
  1728. if (!ARGUMENT_PRESENT( OldContextHandle ))
  1729. {
  1730. ulFlagOptions = FLAG_CONTEXT_REFCOUNT; // indicate that a new context handle was provided from ASC
  1731. }
  1732. Status = SspMapDigestContext(pContext, &Digest, (ulFlagOptions | FLAG_CONTEXT_SERVER), ContextData);
  1733. if (!NT_SUCCESS(Status))
  1734. {
  1735. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext, SspMapContext returns %d\n", Status));
  1736. goto CleanUp;
  1737. }
  1738. *MappedContext = TRUE;
  1739. pContext->ulNC++; // Everything verified so increment to next nonce count
  1740. *NewContextHandle = (ULONG_PTR)pContext; // Just report back with the updated context
  1741. // If this was a re-connect then we need to provide the handle back to calling application
  1742. // This will leave the reference count bumped up one for the re-connect by not calling Release on Context
  1743. if (!ARGUMENT_PRESENT( OldContextHandle ))
  1744. {
  1745. bLockedContext = FALSE; // We no longer own this memory - turned over to CtxtHandler
  1746. }
  1747. *fContextAttributes = fContextAttr; // Return the ASC Attributes set on Context
  1748. if (pExpirationTime)
  1749. {
  1750. *pExpirationTime = pContext->ExpirationTime;
  1751. }
  1752. Status = STATUS_SUCCESS;
  1753. CleanUp:
  1754. // Now perform auditlogon Raid #329545
  1755. if (fGenerateAudit == TRUE)
  1756. {
  1757. if (AuditLogStatus == STATUS_SUCCESS) // Check to see if completed a logon
  1758. {
  1759. if (pContext)
  1760. {
  1761. // get the SID for the logon user
  1762. // this will reduce the number of calls - worse case will not fill in audit buffer
  1763. PSID pSID = NULL;
  1764. CHAR cTokenUser[256];
  1765. PTOKEN_USER pTokenUser = (PTOKEN_USER)cTokenUser;
  1766. if (GetTokenInformation(pContext->TokenHandle, TokenUser,
  1767. pTokenUser, 256, &dwInfoLen))
  1768. {
  1769. pSID = pTokenUser->User.Sid;
  1770. }
  1771. else
  1772. {
  1773. DWORD err = 0;
  1774. err = GetLastError();
  1775. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: GetTokenInfo error 0x%x\n", err));
  1776. }
  1777. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Audit success on user %wZ Domain %wZ pSID 0x%x\n",
  1778. &(pContext->ustrAccountName),&(pContext->ustrDomain), pSID));
  1779. g_LsaFunctions->AuditLogon(
  1780. STATUS_SUCCESS,
  1781. STATUS_SUCCESS,
  1782. &(pContext->ustrAccountName),
  1783. &(pContext->ustrDomain),
  1784. NULL,
  1785. pSID,
  1786. Network,
  1787. &g_DigestSource,
  1788. &(pContext->LoginID)
  1789. );
  1790. if (pTokenUser)
  1791. {
  1792. pSID = NULL;
  1793. }
  1794. }
  1795. }
  1796. else {
  1797. if (pContext)
  1798. { // Select the most appropriate name to display for a failed logon attempt
  1799. pustrAuditAccountName = &(pContext->ustrAccountName);
  1800. pusrtAuditDomain = &(pContext->ustrDomain);
  1801. if (!pustrAuditAccountName->Length)
  1802. {
  1803. pustrAuditAccountName = &(Digest.ustrCrackedAccountName);
  1804. }
  1805. if (!pustrAuditAccountName->Length)
  1806. {
  1807. pustrAuditAccountName = &(Digest.ustrUsername);
  1808. }
  1809. if (!pusrtAuditDomain->Length)
  1810. {
  1811. pusrtAuditDomain = &(Digest.ustrCrackedDomain);
  1812. }
  1813. if (!pusrtAuditDomain->Length)
  1814. {
  1815. pusrtAuditDomain = &(Digest.ustrRealm);
  1816. }
  1817. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: Audit failure on user %wZ Domain %wZ\n",
  1818. pustrAuditAccountName, pusrtAuditDomain));
  1819. g_LsaFunctions->AuditLogon(
  1820. AuditLogStatus,
  1821. AuditLogSubStatus,
  1822. pustrAuditAccountName,
  1823. pusrtAuditDomain,
  1824. NULL,
  1825. NULL,
  1826. Network,
  1827. &g_DigestSource,
  1828. &(pContext->LoginID)
  1829. );
  1830. }
  1831. }
  1832. }
  1833. if (!NT_SUCCESS(Status))
  1834. { // Failed to complete operations so clean up
  1835. if (fLogonSessionCreated == TRUE)
  1836. {
  1837. // Notify LSA that LogonID is not valid
  1838. SubStatus = g_LsaFunctions->DeleteLogonSession(&(pContext->LoginID));
  1839. if (!NT_SUCCESS(SubStatus))
  1840. {
  1841. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: cleanup DeleteLogonSession failed\n"));
  1842. }
  1843. fLogonSessionCreated = FALSE;
  1844. // If we created a token then we need to close it
  1845. if (pContext->TokenHandle)
  1846. {
  1847. SubStatus = NtClose(pContext->TokenHandle);
  1848. pContext->TokenHandle = NULL;
  1849. }
  1850. }
  1851. if (pNewContext)
  1852. {
  1853. ContextFree(pNewContext);
  1854. }
  1855. pNewContext = NULL;
  1856. *NewContextHandle = NULL;
  1857. // Clear up any mapped data
  1858. *MappedContext = FALSE;
  1859. if (ContextData->pvBuffer)
  1860. {
  1861. g_LsaFunctions->FreeLsaHeap(ContextData->pvBuffer);
  1862. ContextData->pvBuffer = NULL;
  1863. ContextData->cbBuffer = 0;
  1864. }
  1865. }
  1866. // DeReference - pCredential, pOldContext
  1867. if (bLockedCredential && pCredential)
  1868. {
  1869. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: CredHandlerRelease to be called for 0x%x\n", pCredential));
  1870. SubStatus = CredHandlerRelease(pCredential);
  1871. if (!NT_SUCCESS(SubStatus))
  1872. {
  1873. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: CredHandlerRelease error 0x%x\n", SubStatus));
  1874. if (NT_SUCCESS(Status))
  1875. {
  1876. Status = SubStatus; // Indicate release error
  1877. }
  1878. }
  1879. }
  1880. if (bLockedContext && pContext)
  1881. {
  1882. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: CtxtHandlerRelease to be called for 0x%x\n", pContext));
  1883. SubStatus = CtxtHandlerRelease(pContext, 1);
  1884. if (!NT_SUCCESS(SubStatus))
  1885. {
  1886. DebugLog((DEB_TRACE, "SpAcceptLsaModeContext: CtxtHandlerRelease error 0x%x\n", SubStatus));
  1887. if (NT_SUCCESS(Status))
  1888. {
  1889. Status = SubStatus; // Indicate release error
  1890. }
  1891. }
  1892. }
  1893. // Free up any allocated memory from the ouput reply buffer
  1894. if (ReplyBuffer.pvBuffer)
  1895. {
  1896. DigestFreeMemory(ReplyBuffer.pvBuffer);
  1897. ReplyBuffer.pvBuffer = NULL;
  1898. ReplyBuffer.cbBuffer = 0;
  1899. }
  1900. // Clean up local memory used by Digest
  1901. DigestFree(&Digest);
  1902. StringFree(&strRealm);
  1903. DebugLog((DEB_TRACE_FUNC, "SpAcceptLsaModeContext: Leaving Context 0x%x Status 0x%x\n", *NewContextHandle, Status));
  1904. return(Status);
  1905. }
  1906. // Creates a logon session for the logontoken contained in the SecurityContext
  1907. // The Token was created for the authenticated digest by ConvertAuthDataToToken
  1908. NTSTATUS
  1909. CtxtCreateLogSess(
  1910. IN PDIGEST_CONTEXT pContext)
  1911. {
  1912. NTSTATUS Status = STATUS_SUCCESS;
  1913. PDIGEST_LOGONSESSION pNewLogonSession = NULL;
  1914. DebugLog((DEB_TRACE_FUNC, "CtxtCreateLogSess: Entering\n"));
  1915. // Create a new entry into LogonSession listing
  1916. pNewLogonSession = (PDIGEST_LOGONSESSION)DigestAllocateMemory(sizeof(DIGEST_LOGONSESSION));
  1917. if (!pNewLogonSession)
  1918. {
  1919. Status = SEC_E_INSUFFICIENT_MEMORY;
  1920. DebugLog((DEB_ERROR, "CtxtCreateLogSess: Could not allocate memory for logonsession, error 0x%x\n", Status));
  1921. goto CleanUp;
  1922. }
  1923. LogonSessionInit(pNewLogonSession);
  1924. pNewLogonSession->LogonType = Network;
  1925. pNewLogonSession->LogonId = pContext->LoginID;
  1926. DebugLog((DEB_TRACE, "CtxtCreateLogSess: Created Digest Logonsession for for LogonID (%x:%lx)\n",
  1927. pContext->LoginID.HighPart, pContext->LoginID.LowPart ));
  1928. Status = UnicodeStringDuplicate(&(pNewLogonSession->ustrAccountName), &(pContext->ustrAccountName));
  1929. if (!NT_SUCCESS (Status))
  1930. {
  1931. DebugLog((DEB_ERROR, "CtxtCreateLogSess: UnicodeStringDuplicate failed status 0x%x\n", Status));
  1932. goto CleanUp;
  1933. }
  1934. Status = UnicodeStringDuplicate(&(pNewLogonSession->ustrDomainName), &(pContext->ustrDomain));
  1935. if (!NT_SUCCESS (Status))
  1936. {
  1937. DebugLog((DEB_ERROR, "CtxtCreateLogSess: UnicodeStringDuplicate failed status 0x%x\n", Status));
  1938. goto CleanUp;
  1939. }
  1940. DebugLog((DEB_TRACE, "CtxtCreateLogSess: Added new logonsession into list, handle 0x%x\n", pNewLogonSession));
  1941. LogSessHandlerInsert(pNewLogonSession);
  1942. pNewLogonSession = NULL; // Turned over memory to LogSessHandler
  1943. CleanUp:
  1944. if (pNewLogonSession)
  1945. {
  1946. (void)LogonSessionFree(pNewLogonSession);
  1947. pNewLogonSession = NULL;
  1948. }
  1949. DebugLog((DEB_TRACE_FUNC, "CtxtCreateLogSess: Leaving Status 0x%x\n", Status));
  1950. return(Status);
  1951. }
  1952. // Creats the Challenge in the server to send back to the client
  1953. //
  1954. // Args: pContext - secruity context to utilize for Challenge Creation
  1955. // pstrRealm - allows for override of the Realm directive by this string
  1956. // OutBuffer - secbuffer to store the output challenge in
  1957. NTSTATUS NTAPI
  1958. ContextCreateChal(
  1959. IN PDIGEST_CONTEXT pContext,
  1960. IN PSTRING pstrRealm,
  1961. OUT PSecBuffer OutBuffer
  1962. )
  1963. {
  1964. NTSTATUS Status = STATUS_SUCCESS;
  1965. ULONG cbLenNeeded = 0;
  1966. BOOL fSASLMode = FALSE;
  1967. STRING strTempRealm = {0};
  1968. PCHAR pczTemp = NULL;
  1969. PCHAR pczTemp2 = NULL;
  1970. DebugLog((DEB_TRACE_FUNC, "ContextCreateChal: Entering\n"));
  1971. // allocate the buffers for output - in the future can optimze to allocate exact amount needed
  1972. pczTemp = (PCHAR)DigestAllocateMemory((3 * NTDIGEST_SP_MAX_TOKEN_SIZE) + 1);
  1973. if (!pczTemp)
  1974. {
  1975. DebugLog((DEB_ERROR, "ContextCreateChal: No memory for output buffers\n"));
  1976. goto CleanUp;
  1977. }
  1978. pczTemp2 = (PCHAR)DigestAllocateMemory(NTDIGEST_SP_MAX_TOKEN_SIZE + 1);
  1979. if (!pczTemp2)
  1980. {
  1981. DebugLog((DEB_ERROR, "ContextCreateChal: No memory for output buffers\n"));
  1982. goto CleanUp;
  1983. }
  1984. pczTemp[0] = '\0';
  1985. pczTemp2[0] = '\0';
  1986. // Check to make sure we have minimal input and outputs
  1987. if ((!pContext) || (!OutBuffer) || (!pstrRealm))
  1988. {
  1989. Status = STATUS_INVALID_PARAMETER;
  1990. DebugLog((DEB_ERROR, "ContextCreateChal: Invalid args\n"));
  1991. goto CleanUp;
  1992. }
  1993. // calculate the MAX possible size for the respose - will be smaller than this value
  1994. cbLenNeeded = CB_CHAL; // MAX byte count for directives and symbols
  1995. cbLenNeeded += pContext->strNonce.Length;
  1996. cbLenNeeded += pContext->strOpaque.Length;
  1997. cbLenNeeded += (pstrRealm->Length * 2);
  1998. cbLenNeeded += (g_strNtDigestUTF8ServerRealm.Length * 2); // account for max backslash encode by * 2
  1999. cbLenNeeded += (g_strNTDigestISO8859ServerRealm.Length * 2); // Really only need one of these but make simple math
  2000. if (cbLenNeeded > NTDIGEST_SP_MAX_TOKEN_CHALLENGE_SIZE)
  2001. {
  2002. Status = STATUS_BUFFER_TOO_SMALL;
  2003. DebugLog((DEB_ERROR, "ContextCreateChal: challenge exceeded max size len is %d\n", cbLenNeeded));
  2004. goto CleanUp;
  2005. }
  2006. if (pContext->typeDigest == SASL_SERVER)
  2007. {
  2008. fSASLMode = TRUE;
  2009. }
  2010. if (pContext->typeQOP == AUTH_CONF)
  2011. {
  2012. sprintf(pczTemp, "qop=\"auth,auth-int,auth-conf\",cipher=\"3des,des,rc4-40,rc4,rc4-56\",algorithm=%s,nonce=\"%Z\"",
  2013. ((fSASLMode == TRUE) ? MD5_SESS_SASLSTR: MD5_SESSSTR), &pContext->strNonce);
  2014. }
  2015. else if (pContext->typeQOP == AUTH_INT)
  2016. {
  2017. sprintf(pczTemp, "qop=\"auth,auth-int\",algorithm=%s,nonce=\"%Z\"",
  2018. ((fSASLMode == TRUE) ? MD5_SESS_SASLSTR: MD5_SESSSTR), &pContext->strNonce);
  2019. }
  2020. else
  2021. {
  2022. sprintf(pczTemp, "qop=\"auth\",algorithm=%s,nonce=\"%Z\"",
  2023. ((fSASLMode == TRUE) ? MD5_SESS_SASLSTR: MD5_SESSSTR), &pContext->strNonce);
  2024. }
  2025. // Attach opaque data (but not on SASL_SERVER)
  2026. if ((pContext->strOpaque.Length) && (pContext->typeDigest != SASL_SERVER))
  2027. {
  2028. sprintf(pczTemp2, ",opaque=\"%Z\"", &pContext->strOpaque);
  2029. strcat(pczTemp, pczTemp2);
  2030. }
  2031. // Attach charset to allow UTF-8 character encoding
  2032. if (pContext->typeCharset == UTF_8)
  2033. {
  2034. strcat(pczTemp, ",charset=utf-8");
  2035. }
  2036. // Attach realm - allow the strRealm to override the system DnsDomainName
  2037. if (pstrRealm->Buffer)
  2038. {
  2039. if (pstrRealm->Length)
  2040. {
  2041. Status = BackslashEncodeString(pstrRealm, &strTempRealm);
  2042. if (!NT_SUCCESS (Status))
  2043. {
  2044. DebugLog((DEB_ERROR, "ContextCreateChal: BackslashEncode failed status 0x%x\n", Status));
  2045. goto CleanUp;
  2046. }
  2047. DebugLog((DEB_TRACE, "ContextCreateChal: Realm provided (%Z) backslash encoded (%Z)\n", pstrRealm, &strTempRealm));
  2048. sprintf(pczTemp2, ",realm=\"%Z\"", &strTempRealm);
  2049. strcat(pczTemp, pczTemp2);
  2050. }
  2051. else
  2052. {
  2053. // valid buffer zero length is a blank string for the realm
  2054. strcat(pczTemp, ",realm=\"\"");
  2055. }
  2056. }
  2057. else
  2058. {
  2059. // determine the realm to present based on charset requested - SASL only; HTTP always us 8859-1
  2060. if ((fSASLMode == TRUE) && (pContext->typeCharset == UTF_8))
  2061. {
  2062. if (g_strNtDigestUTF8ServerRealm.Length)
  2063. {
  2064. Status = BackslashEncodeString(&g_strNtDigestUTF8ServerRealm, &strTempRealm);
  2065. if (!NT_SUCCESS (Status))
  2066. {
  2067. DebugLog((DEB_ERROR, "ContextCreateChal: BackslashEncode failed status 0x%x\n", Status));
  2068. goto CleanUp;
  2069. }
  2070. DebugLog((DEB_TRACE, "ContextCreateChal: UTF-8 default Realm (%Z) backslash encoded (%Z)\n",
  2071. &g_strNtDigestUTF8ServerRealm, &strTempRealm));
  2072. sprintf(pczTemp2, ",realm=\"%Z\"", &strTempRealm);
  2073. strcat(pczTemp, pczTemp2);
  2074. }
  2075. }
  2076. else
  2077. {
  2078. /*
  2079. if (g_strNTDigestISO8859ServerRealm.Length)
  2080. {
  2081. Status = BackslashEncodeString(&g_strNTDigestISO8859ServerRealm, &strTempRealm);
  2082. if (!NT_SUCCESS (Status))
  2083. {
  2084. DebugLog((DEB_ERROR, "ContextCreateChal: BackslashEncode failed status 0x%x\n", Status));
  2085. goto CleanUp;
  2086. }
  2087. DebugLog((DEB_TRACE, "ContextCreateChal: ISO 8859-1 default Realm (%Z) backslash encoded (%Z)\n",
  2088. &g_strNTDigestISO8859ServerRealm, &strTempRealm));
  2089. sprintf(pczTemp2, ",realm=\"%Z\"", &strTempRealm);
  2090. strcat(pczTemp, pczTemp2);
  2091. }
  2092. */
  2093. sprintf(pczTemp2, ",realm=\"%s\"", STR_DIGEST_DOMAIN);
  2094. strcat(pczTemp, pczTemp2);
  2095. }
  2096. }
  2097. // Attach stale directive if indicated
  2098. // if (some method to determine if context has expired)
  2099. // {
  2100. // sprintf(pczTemp2, ",stale=true");
  2101. // strcat(pczTemp, pczTemp2);
  2102. // }
  2103. // total buffer for Challenge (NULL is not included in output buffer - ref:Bug 310201)
  2104. // cbLenNeeded = strlen(pczTemp) + sizeof(CHAR);
  2105. cbLenNeeded = (USHORT)strlen(pczTemp);
  2106. // Check on allocating output buffer
  2107. if (!OutBuffer->cbBuffer)
  2108. {
  2109. OutBuffer->pvBuffer = DigestAllocateMemory(cbLenNeeded);
  2110. if (!OutBuffer->pvBuffer)
  2111. {
  2112. Status = SEC_E_INSUFFICIENT_MEMORY;
  2113. DebugLog((DEB_ERROR, "ContextCreateChal: out of memory on challenge output\n"));
  2114. goto CleanUp;
  2115. }
  2116. OutBuffer->cbBuffer = cbLenNeeded;
  2117. OutBuffer->BufferType = SECBUFFER_DATA;
  2118. }
  2119. if (cbLenNeeded > OutBuffer->cbBuffer)
  2120. {
  2121. Status = STATUS_BUFFER_TOO_SMALL;
  2122. DebugLog((DEB_ERROR, "ContextCreateChal: output buffer too small need %d len is %d\n",
  2123. cbLenNeeded, OutBuffer->cbBuffer));
  2124. goto CleanUp;
  2125. }
  2126. memcpy(OutBuffer->pvBuffer, pczTemp, cbLenNeeded);
  2127. // Now indicate number of bytes utilized in output buffer
  2128. OutBuffer->cbBuffer = cbLenNeeded;
  2129. CleanUp:
  2130. if (pczTemp)
  2131. {
  2132. DigestFreeMemory(pczTemp);
  2133. pczTemp = NULL;
  2134. }
  2135. if (pczTemp2)
  2136. {
  2137. DigestFreeMemory(pczTemp2);
  2138. pczTemp2 = NULL;
  2139. }
  2140. StringFree(&strTempRealm);
  2141. DebugLog((DEB_TRACE_FUNC, "ContextCreateChal: Leaving Status 0x%x\n", Status));
  2142. return(Status);
  2143. }
  2144. // This is the main section to process a Context with an incoming Digest message to authenticate the
  2145. // message on the DC, generate a session key, and get the user Token. On subsequent calls, the session key
  2146. // can be utilized directly and if the Digest is authenticated, the Token can be utilized.
  2147. // AuditLogStatus can be used to provide SubStatus in AuditLogging on server
  2148. NTSTATUS NTAPI
  2149. DigestProcessParameters(
  2150. IN OUT PDIGEST_CONTEXT pContext,
  2151. IN PDIGEST_PARAMETER pDigest,
  2152. OUT PSecBuffer pOutputToken,
  2153. OUT PNTSTATUS pAuditLogStatus,
  2154. OUT PNTSTATUS pAuditLogSubStatus,
  2155. OUT PBOOL pfGenerateAudit)
  2156. {
  2157. NTSTATUS Status = STATUS_SUCCESS;
  2158. NTSTATUS SubStatus = STATUS_SUCCESS;
  2159. ULONG ulNonceCount = 0;
  2160. USHORT cbDigestParamEncoded = 0; // Contains the number of bytes in Request to send out
  2161. BOOL fLogonSessionCreated = FALSE; // indicate if the LSA was notified about logon
  2162. // Encoded Digest Parameters to send over Generic Passthrough
  2163. BYTE *pDigestParamEncoded = NULL;
  2164. // Generic Passthrough variables - used to send data to DC for digest verification
  2165. UNICODE_STRING MsvPackageName = CONSTANT_UNICODE_STRING(TEXT(MSV1_0_PACKAGE_NAME));
  2166. PMSV1_0_PASSTHROUGH_REQUEST PassthroughRequest = NULL;
  2167. PMSV1_0_PASSTHROUGH_RESPONSE PassthroughResponse = NULL;
  2168. ULONG RequestSize = 0;
  2169. ULONG ResponseSize = 0;
  2170. ULONG ulAuthDataSize = 0;
  2171. PUCHAR Where = NULL;
  2172. PWCHAR pwczTempName = NULL;
  2173. BOOL fDigestValid = FALSE; // is the returned digest response valid (contains authdata?)
  2174. // AuthData to Logon Token Variables
  2175. SECURITY_LOGON_TYPE LogonType = Network;
  2176. SECURITY_IMPERSONATION_LEVEL ImpersonationLevel = SecurityImpersonation;
  2177. PUNICODE_STRING pustrTempDomain = NULL;
  2178. UNICODE_STRING ustrAccountName = {0};
  2179. PUCHAR puTempAuth = NULL;
  2180. DebugLog((DEB_TRACE_FUNC, "DigestProcessParameters: Entering\n"));
  2181. ASSERT(pAuditLogStatus);
  2182. ASSERT(pAuditLogSubStatus);
  2183. ASSERT(pfGenerateAudit);
  2184. *pfGenerateAudit = FALSE;
  2185. *pAuditLogStatus = STATUS_LOGON_FAILURE;
  2186. *pAuditLogSubStatus = STATUS_LOGON_FAILURE;
  2187. // Copy over the context types into the digest structure
  2188. pDigest->typeAlgorithm = pContext->typeAlgorithm;
  2189. pDigest->typeDigest = pContext->typeDigest;
  2190. pDigest->typeQOP = pContext->typeQOP;
  2191. pDigest->typeCharset = pContext->typeCharset;
  2192. // Check to make sure that the nonce sent back originated from this machine and is valid
  2193. Status = NonceIsValid(&(pDigest->refstrParam[MD5_AUTH_NONCE]));
  2194. if (!NT_SUCCESS(Status))
  2195. {
  2196. Status = STATUS_INVALID_PARAMETER;
  2197. DebugLog((DEB_ERROR, "DigestProcessParameters: Nonce is not valid\n"));
  2198. goto CleanUp;
  2199. }
  2200. // Make sure that the nonces are the same
  2201. if (RtlCompareString(&(pContext->strNonce), &(pDigest->refstrParam[MD5_AUTH_NONCE]), FALSE))
  2202. {
  2203. Status = STATUS_INVALID_PARAMETER;
  2204. DebugLog((DEB_ERROR, "DigestProcessParameters: nonce does not mach Context nonce!\n"));
  2205. goto CleanUp;
  2206. }
  2207. // We must have a noncecount specified since we specified a qop in the Challenge
  2208. // If we decide to support no noncecount modes then we need to make sure that qop is not specified
  2209. if (pDigest->refstrParam[MD5_AUTH_NC].Length)
  2210. {
  2211. Status = RtlCharToInteger(pDigest->refstrParam[MD5_AUTH_NC].Buffer, HEXBASE, &ulNonceCount);
  2212. if (!NT_SUCCESS(Status))
  2213. {
  2214. Status = STATUS_INVALID_PARAMETER;
  2215. DebugLog((DEB_ERROR, "DigestProcessParameters: Nonce Count badly formatted\n"));
  2216. goto CleanUp;
  2217. }
  2218. }
  2219. else
  2220. {
  2221. Status = STATUS_INVALID_PARAMETER;
  2222. DebugLog((DEB_ERROR, "DigestProcessParameters: Nonce Count not specified\n"));
  2223. goto CleanUp;
  2224. }
  2225. // Check nonceCount is incremented to preclude replay
  2226. if (ulNonceCount < (pContext->ulNC + 1))
  2227. {
  2228. // We failed to verify next noncecount
  2229. Status = SEC_E_OUT_OF_SEQUENCE;
  2230. DebugLog((DEB_ERROR, "DigestProcessParameters: NonceCount failed to increment!\n"));
  2231. goto CleanUp;
  2232. }
  2233. // Verify that this context matches the content in the Digest Parameters
  2234. // We have already gone to the DC and authenticated the first message
  2235. if (pContext->strSessionKey.Length)
  2236. {
  2237. DebugLog((DEB_TRACE, "DigestProcessParameters: We have a previous session key - use key for auth\n"));
  2238. // Copy the SessionKey from the Context into the Digest Structure to verify against
  2239. // This will have Digest Auth routines use the SessionKey rather than recompute H(A1)
  2240. StringFree(&(pDigest->strSessionKey));
  2241. Status = StringDuplicate(&(pDigest->strSessionKey), &(pContext->strSessionKey));
  2242. if (!NT_SUCCESS(Status))
  2243. {
  2244. DebugLog((DEB_ERROR, "DigestProcessParameters: Failed to copy over SessionKey\n"));
  2245. goto CleanUp;
  2246. }
  2247. // No check locally that Digest is authentic
  2248. Status = DigestCalculation(pDigest, NULL);
  2249. if (!NT_SUCCESS(Status))
  2250. {
  2251. DebugLog((DEB_ERROR, "DigestProcessParameters: Oh no we FAILED Authentication!!!!\n"));
  2252. goto CleanUp;
  2253. }
  2254. // We have an authenticated the request
  2255. // Can utilize logonID
  2256. pContext->ulNC = ulNonceCount; // Indicate that we have processed up to this NC
  2257. }
  2258. else
  2259. {
  2260. DebugLog((DEB_TRACE, "DigestProcessParameters: No session key - call DC for auth\n"));
  2261. Status = DigestDecodeDirectiveStrings(pDigest);
  2262. if (!NT_SUCCESS (Status))
  2263. {
  2264. DebugLog((DEB_ERROR, "SpAcceptLsaModeContext: DigestDecodeDirectiveStrings error 0x%x\n", Status));
  2265. goto CleanUp;
  2266. }
  2267. // Try to process the accountname and domain for the user's DC
  2268. Status = DigestDecodeUserAccount(pDigest);
  2269. if (!NT_SUCCESS (Status))
  2270. {
  2271. DebugLog((DEB_ERROR, "DigestProcessParameters: DigestDecodeUserAccount failed 0x%x\n", Status));
  2272. goto CleanUp;
  2273. }
  2274. // Always go to the server's DC since you will need to expand the group membership after getting user's group info
  2275. pDigest->usFlags |= FLAG_SERVERS_DOMAIN;
  2276. DebugLog((DEB_TRACE, "DigestProcessParameters: GenericPassthrough to domain [%wZ]\n", &(g_NtDigestSecPkg.DomainName)));
  2277. // Serialize the Digest Parameters (if need to send off box)
  2278. cbDigestParamEncoded = 0; // Will be allocated by BlobEncodeRequest
  2279. Status = BlobEncodeRequest(pDigest, &pDigestParamEncoded, &cbDigestParamEncoded);
  2280. if (!NT_SUCCESS (Status))
  2281. {
  2282. DebugLog((DEB_ERROR, "DigestProcessParameters: BlobEncodeRequest failed 0x%x\n", Status));
  2283. goto CleanUp;
  2284. }
  2285. // Send the Serialized Digest to the DC for verification & return of validity & session key
  2286. // If paramters match, perform authentication locally and utilize previous token
  2287. //
  2288. // We have to pass off to the DC so build the request.
  2289. //
  2290. RequestSize = sizeof(MSV1_0_PASSTHROUGH_REQUEST) +
  2291. (g_NtDigestSecPkg.DomainName).Length +
  2292. g_ustrNtDigestPackageName.Length +
  2293. cbDigestParamEncoded;
  2294. PassthroughRequest = (PMSV1_0_PASSTHROUGH_REQUEST) DigestAllocateMemory(RequestSize);
  2295. if (PassthroughRequest == NULL)
  2296. {
  2297. Status = STATUS_INSUFFICIENT_RESOURCES;
  2298. goto CleanUp;
  2299. }
  2300. Where = (PUCHAR) (PassthroughRequest + 1); // Points to byte AFTER passthrough header
  2301. PassthroughRequest->MessageType = MsV1_0GenericPassthrough;
  2302. PassthroughRequest->DomainName.Length = (g_NtDigestSecPkg.DomainName).Length;
  2303. PassthroughRequest->DomainName.MaximumLength = (g_NtDigestSecPkg.DomainName).Length;
  2304. PassthroughRequest->DomainName.Buffer = (LPWSTR) Where; // this is NOT NULL TERMINATED
  2305. RtlCopyMemory(
  2306. Where,
  2307. (g_NtDigestSecPkg.DomainName).Buffer,
  2308. (g_NtDigestSecPkg.DomainName).Length
  2309. );
  2310. Where += (g_NtDigestSecPkg.DomainName).Length;
  2311. PassthroughRequest->PackageName.Length = g_ustrNtDigestPackageName.Length;
  2312. PassthroughRequest->PackageName.MaximumLength = g_ustrNtDigestPackageName.Length;
  2313. PassthroughRequest->PackageName.Buffer = (LPWSTR) Where; // Not NULL terminated - relative reference
  2314. RtlCopyMemory(
  2315. Where,
  2316. g_ustrNtDigestPackageName.Buffer,
  2317. g_ustrNtDigestPackageName.Length
  2318. );
  2319. Where += g_ustrNtDigestPackageName.Length;
  2320. PassthroughRequest->LogonData = Where;
  2321. PassthroughRequest->DataLength = (ULONG)cbDigestParamEncoded;
  2322. RtlCopyMemory(
  2323. Where,
  2324. pDigestParamEncoded,
  2325. cbDigestParamEncoded
  2326. );
  2327. //
  2328. // We've build the buffer, now call NTLM to pass it through.
  2329. //
  2330. Status = g_LsaFunctions->CallPackage(
  2331. &MsvPackageName,
  2332. PassthroughRequest,
  2333. RequestSize, // How many bytes to send in Request
  2334. (PVOID *) &PassthroughResponse, // Place the buffers here
  2335. &ResponseSize, // Passed back the size of the buffer
  2336. &SubStatus // Return code from Digest Auth on the DC
  2337. );
  2338. DebugLog((DEB_TRACE, "DigestProcessParameters: Server passthrough Responsesize %ld, Status 0x%x, SubStatus 0x%x\n",
  2339. ResponseSize, Status, SubStatus));
  2340. if (!NT_SUCCESS(Status))
  2341. {
  2342. DebugLog((DEB_ERROR,"DigestProcessParameters: Failed to call MSV package to verify Digest: 0x%x\n",Status));
  2343. if (Status == STATUS_INVALID_INFO_CLASS)
  2344. {
  2345. Status = STATUS_LOGON_FAILURE;
  2346. }
  2347. goto CleanUp;
  2348. }
  2349. *pfGenerateAudit = TRUE;
  2350. if (!NT_SUCCESS(SubStatus))
  2351. {
  2352. Status = SubStatus;
  2353. // Expand the DCs error code into the proper Status and SubStatus (lsa\server\cfiles\adtevent.c)
  2354. switch(SubStatus){
  2355. case STATUS_ACCOUNT_EXPIRED:
  2356. *pAuditLogStatus = STATUS_ACCOUNT_EXPIRED;
  2357. break;
  2358. case STATUS_ACCOUNT_LOCKED_OUT:
  2359. *pAuditLogStatus = STATUS_ACCOUNT_LOCKED_OUT;
  2360. break;
  2361. case STATUS_LOGON_TYPE_NOT_GRANTED:
  2362. *pAuditLogStatus = STATUS_ACCOUNT_EXPIRED;
  2363. break;
  2364. case STATUS_PASSWORD_MUST_CHANGE:
  2365. *pAuditLogStatus = STATUS_PASSWORD_MUST_CHANGE;
  2366. break;
  2367. case STATUS_PASSWORD_EXPIRED:
  2368. case STATUS_ACCOUNT_DISABLED:
  2369. case STATUS_INVALID_LOGON_HOURS:
  2370. case STATUS_INVALID_WORKSTATION:
  2371. *pAuditLogStatus = STATUS_ACCOUNT_RESTRICTION;
  2372. *pAuditLogSubStatus = SubStatus;
  2373. break;
  2374. default:
  2375. *pAuditLogStatus = STATUS_LOGON_FAILURE;
  2376. *pAuditLogSubStatus = SubStatus;
  2377. }
  2378. DebugLog((DEB_ERROR,"DigestProcessParameters: DC failed to verify Digest Response: 0x%x\n",Status));
  2379. goto CleanUp;
  2380. }
  2381. if (!PassthroughResponse || (ResponseSize == 0) || (PassthroughResponse->DataLength == 0))
  2382. {
  2383. DebugLog((DEB_ERROR,"DigestProcessParameters: PassthroughResponse contains no valid data\n"));
  2384. Status = STATUS_INTERNAL_ERROR;
  2385. goto CleanUp;
  2386. }
  2387. DebugLog((DEB_TRACE, "DigestProcessParameters: PassthroughResponse->DataLength %ld\n",
  2388. PassthroughResponse->DataLength));
  2389. Where = (PUCHAR) (PassthroughResponse + 1); // start copy after header
  2390. Status = DigestDecodeResponse(PassthroughResponse->DataLength,
  2391. Where,
  2392. &fDigestValid,
  2393. &ulAuthDataSize,
  2394. &puTempAuth,
  2395. &(pContext->strSessionKey),
  2396. &(pContext->ustrAccountName),
  2397. &(pContext->ustrDomain));
  2398. if (!NT_SUCCESS(Status))
  2399. {
  2400. DebugLog((DEB_ERROR,"DigestProcessParameters: Failed DigestDecodeResponse 0x%x\n",Status));
  2401. goto CleanUp;
  2402. }
  2403. DebugLog((DEB_TRACE,"DigestProcessParameters: Processing DC Response\n"));
  2404. // If authenticated then, create a logon token with the DC returns (unless previous token exists)
  2405. if (fDigestValid == FALSE)
  2406. {
  2407. Status = STATUS_INTERNAL_ERROR;
  2408. DebugLog((DEB_ERROR,"DigestProcessParameters: Digest Response contains no authdata 0x%x\n",Status));
  2409. goto CleanUp;
  2410. }
  2411. // Filter the Sids on the member boundary
  2412. // The following have not been set so they are NULL (or zero)
  2413. ASSERT(!pDigest->ulTrustDirection);
  2414. ASSERT(!pDigest->ulTrustType);
  2415. ASSERT(!pDigest->ulTrustAttributes);
  2416. ASSERT(!pDigest->pTrustSid);
  2417. Status = DigestCheckPacForSidFiltering(pDigest, &puTempAuth, &ulAuthDataSize);
  2418. if (!NT_SUCCESS(Status))
  2419. {
  2420. DebugLog((DEB_ERROR, "DigestProcessParameters: Failed member boundary SID filtering Status 0x%x\n",
  2421. Status));
  2422. StringFree(&(pContext->strSessionKey)); // clear out any password hashed data since not needed
  2423. goto CleanUp;
  2424. }
  2425. DebugLog((DEB_TRACE, "DigestProcessParameters: member boundary sid filtering completed\n"));
  2426. // Now create the logon token with the AuthData buffer
  2427. // LsaConvertAuthDataToToken()
  2428. // Set the AuthorityName to the DC's Domainname
  2429. // g_DigestSource established at SpInitialize time in the LSA
  2430. if (pContext->ustrDomain.Length)
  2431. {
  2432. pustrTempDomain = &(pContext->ustrDomain);
  2433. }
  2434. Status = g_LsaFunctions->ConvertAuthDataToToken(puTempAuth, ulAuthDataSize,
  2435. ImpersonationLevel, &g_DigestSource, LogonType, pustrTempDomain,
  2436. &(pContext->TokenHandle), &(pContext->LoginID), &ustrAccountName, &SubStatus);
  2437. if (!NT_SUCCESS(Status))
  2438. {
  2439. DebugLog((DEB_ERROR, "DigestProcessParameters: Failed to form token from AuthData 0x%x subStatus 0x%x\n",
  2440. Status, SubStatus));
  2441. pContext->TokenHandle = NULL; // no valid handle returned
  2442. StringFree(&(pContext->strSessionKey)); // clear out any password hashed data since not needed
  2443. goto CleanUp;
  2444. }
  2445. fLogonSessionCreated = TRUE; // LSA notified about LogonID
  2446. DebugLog((DEB_TRACE, "DigestProcessParameters: Token Created Handle 0x%x, LogonID (%x:%lx) \n",
  2447. pContext->TokenHandle, pContext->LoginID.HighPart, pContext->LoginID.LowPart));
  2448. DebugLog((DEB_TRACE, "DigestProcessParameters: Token AccountName %wZ \n", &ustrAccountName));
  2449. DebugLog((DEB_TRACE, " Session Key: %.10Z**********\n", &(pContext->strSessionKey)));
  2450. pContext->ulNC = ulNonceCount; // Indicate that we have processed up to this NC
  2451. if (pContext->typeDigest == SASL_SERVER)
  2452. {
  2453. // Form the ResponseAuth according to RFC2831 Sect 2.1.3
  2454. StringFree(&pDigest->strSessionKey);
  2455. Status = StringDuplicate(&pDigest->strSessionKey, &pContext->strSessionKey);
  2456. if (!NT_SUCCESS(Status))
  2457. {
  2458. DebugLog((DEB_ERROR, "DigestProcessParameters: Failed to copy session key\n"));
  2459. goto CleanUp;
  2460. }
  2461. Status = DigestSASLResponseAuth(pDigest, pOutputToken);
  2462. if (!NT_SUCCESS(Status))
  2463. {
  2464. DebugLog((DEB_ERROR, "DigestProcessParameters: Failed to generate ResponseAuth\n"));
  2465. goto CleanUp;
  2466. }
  2467. }
  2468. }
  2469. // Token created - Logon OK
  2470. *pAuditLogStatus = STATUS_SUCCESS;
  2471. *pAuditLogSubStatus = STATUS_SUCCESS;
  2472. CleanUp:
  2473. BlobFreeRequest(pDigestParamEncoded);
  2474. if (!NT_SUCCESS(Status))
  2475. {
  2476. // If we failed - do extra cleanup
  2477. if (fLogonSessionCreated == TRUE)
  2478. {
  2479. // Notify LSA that LogonID is not valid
  2480. SubStatus = g_LsaFunctions->DeleteLogonSession(&(pContext->LoginID));
  2481. if (!NT_SUCCESS(SubStatus))
  2482. {
  2483. DebugLog((DEB_ERROR, "DigestProcessParameters: cleanup DeleteLogonSession failed\n"));
  2484. }
  2485. fLogonSessionCreated = FALSE;
  2486. }
  2487. // If we created a token then we need to close it
  2488. if (pContext->TokenHandle)
  2489. {
  2490. SubStatus = NtClose(pContext->TokenHandle);
  2491. pContext->TokenHandle = NULL;
  2492. }
  2493. }
  2494. if (PassthroughRequest != NULL)
  2495. {
  2496. DigestFreeMemory(PassthroughRequest);
  2497. }
  2498. if (PassthroughResponse != NULL)
  2499. {
  2500. g_LsaFunctions->FreeReturnBuffer(PassthroughResponse);
  2501. }
  2502. if (ustrAccountName.Buffer)
  2503. { // Need to free up memory from token creation
  2504. g_LsaFunctions->FreeLsaHeap(ustrAccountName.Buffer);
  2505. ustrAccountName.Buffer = NULL;
  2506. ustrAccountName.Length = ustrAccountName.MaximumLength = 0;
  2507. }
  2508. DigestFreeMemory(puTempAuth);
  2509. DigestFreeMemory(pwczTempName);
  2510. DebugLog((DEB_TRACE_FUNC, "DigestProcessParameters: Leaving Status 0x%x\n", Status));
  2511. return(Status);
  2512. }
  2513. // This call is utilized by Initialize Securitycontext - it is used to create the sessionkey
  2514. // form the response hash. This function is called only as a client process
  2515. NTSTATUS NTAPI
  2516. DigestGenerateParameters(
  2517. IN OUT PDIGEST_CONTEXT pContext,
  2518. IN PDIGEST_PARAMETER pDigest,
  2519. OUT PSecBuffer pOutputToken)
  2520. {
  2521. NTSTATUS Status = STATUS_SUCCESS;
  2522. UNICODE_STRING ustrTempPasswd = {0};
  2523. USER_CREDENTIALS UserCreds;
  2524. ZeroMemory(&UserCreds, sizeof(UserCreds));
  2525. DebugLog((DEB_TRACE_FUNC, "DigestGenerateParameters: Entering\n"));
  2526. pDigest->typeDigest = pContext->typeDigest;
  2527. pDigest->typeAlgorithm = pContext->typeAlgorithm;
  2528. pDigest->typeQOP = pContext->typeQOP;
  2529. pDigest->typeCipher = pContext->typeCipher;
  2530. pDigest->typeCharset = pContext->typeCharset;
  2531. if (pContext->ulFlags & FLAG_CONTEXT_QUOTE_QOP)
  2532. {
  2533. pDigest->usFlags |= FLAG_QUOTE_QOP;
  2534. }
  2535. // We must have specified the username and password
  2536. Status = UnicodeStringDuplicate(&(UserCreds.ustrRealm), &(pContext->ustrDomain));
  2537. if (!NT_SUCCESS(Status))
  2538. {
  2539. DebugLog((DEB_ERROR, "DigestGenerateParameters: Duplicate Domain string status 0x%x\n", Status));
  2540. goto CleanUp;
  2541. }
  2542. Status = UnicodeStringDuplicate(&(UserCreds.ustrUsername), &(pContext->ustrAccountName));
  2543. if (!NT_SUCCESS(Status))
  2544. {
  2545. DebugLog((DEB_ERROR, "DigestGenerateParameters: Duplicate Username string status 0x%x\n", Status));
  2546. goto CleanUp;
  2547. }
  2548. Status = UnicodeStringDuplicatePassword(&(UserCreds.ustrPasswd), &(pContext->ustrPassword));
  2549. if (!NT_SUCCESS(Status))
  2550. {
  2551. DebugLog((DEB_ERROR, "DigestGenerateParameters: Duplicate Password string status 0x%x\n", Status));
  2552. goto CleanUp;
  2553. }
  2554. UserCreds.fIsValidPasswd = TRUE;
  2555. UserCreds.fIsEncryptedPasswd = TRUE;
  2556. DebugLog((DEB_TRACE, "DigestGenerateParameters: Before DigestCalculation\n"));
  2557. (void)DigestPrint(pDigest);
  2558. // Caclulcate the response value - we are in an ISC call
  2559. Status = DigestCalculation(pDigest, &UserCreds);
  2560. if (!NT_SUCCESS(Status))
  2561. {
  2562. DebugLog((DEB_ERROR, "DigestGenerateParameters: Could not create Response status 0x%x\n", Status));
  2563. goto CleanUp;
  2564. }
  2565. // DigestCalculation determined the sessionkey - copy into this context
  2566. StringFree(&(pContext->strSessionKey));
  2567. Status = StringDuplicate( &(pContext->strSessionKey), &(pDigest->strSessionKey));
  2568. if (!NT_SUCCESS(Status))
  2569. {
  2570. DebugLog((DEB_ERROR, "DigestGenerateParameters: Failed to copy over SessionKey\n"));
  2571. goto CleanUp;
  2572. }
  2573. // We have an authenticated the request
  2574. // Can utilize logonID
  2575. Status = DigestCreateChalResp(pDigest, &UserCreds, pOutputToken);
  2576. if (!NT_SUCCESS(Status))
  2577. {
  2578. DebugLog((DEB_ERROR, "DigestGenerateParameters: Failed to create Output String status 0x%x\n", Status));
  2579. goto CleanUp;
  2580. }
  2581. DebugLog((DEB_TRACE, "DigestGenerateParameters: After DigestCalculation & copy struct\n"));
  2582. (void)DigestPrint(pDigest);
  2583. CleanUp:
  2584. UserCredentialsFree(&UserCreds);
  2585. UnicodeStringFree(&ustrTempPasswd);
  2586. DebugLog((DEB_TRACE_FUNC, "DigestGenerateParameters: Leaving\n"));
  2587. return(Status);
  2588. }
  2589. // Called by digest (inside LSA) with a buffer routed from a server to the DC running this code
  2590. // We need to strip out the header and extract the DIGEST_BLOB_REQUEST
  2591. //
  2592. // pcbMessageRequest will return the number of bytes allocated for response
  2593. // ppMessageResponse will contain the pointer to the allocated buffer
  2594. // calling routine must free the buffer (DigestFreeMemory) after it is done with it
  2595. NTSTATUS NTAPI
  2596. DigestPackagePassthrough(IN USHORT cbMessageRequest,
  2597. IN BYTE *pMessageRequest,
  2598. IN OUT ULONG *pulMessageResponse,
  2599. OUT PBYTE *ppMessageResponse)
  2600. {
  2601. NTSTATUS Status = STATUS_SUCCESS;
  2602. ASSERT(pMessageRequest);
  2603. ASSERT(pulMessageResponse);
  2604. ASSERT(ppMessageResponse);
  2605. DebugLog((DEB_TRACE_FUNC, "DigestPackagePassthrough: Entering\n"));
  2606. // Function will allocate space for Response - we need to free it after use
  2607. Status = DigestResponseBru(cbMessageRequest, pMessageRequest, pulMessageResponse, ppMessageResponse);
  2608. if (!NT_SUCCESS(Status))
  2609. {
  2610. DebugLog((DEB_ERROR, "DigestPackagePassthrough: Error with DigestVerifyResponseBru\n"));
  2611. goto CleanUp;
  2612. }
  2613. CleanUp:
  2614. DebugLog((DEB_TRACE_FUNC, "DigestPackagePassthrough: Leaving\n"));
  2615. return(Status);
  2616. }
  2617. // Routine receives the DigestBlob to process by extracting the password
  2618. // and verifying the response-value. If authenticated, the SessionKey can be returned
  2619. // to the server for future authentication
  2620. //
  2621. // This routine runs under LSA on the DC. It will do the actual Digest auth and return session keys
  2622. //
  2623. // pcbResponse is a pointer to a USHORT which holds amount of bytes in PResponse
  2624. // it also returns the number of bytes actually used
  2625. // The buffer will be allocated in this routine by DigestAllocateMemory and must be freed by DigestFreeMemory by
  2626. // calling routine
  2627. NTSTATUS NTAPI
  2628. DigestResponseBru(
  2629. IN USHORT cbMessageRequest,
  2630. IN BYTE *pMessageRequest,
  2631. OUT PULONG pulResponse,
  2632. OUT PBYTE *ppResponse)
  2633. {
  2634. NTSTATUS Status = STATUS_SUCCESS;
  2635. NTSTATUS StatusSub = STATUS_LOGON_FAILURE;
  2636. DIGEST_PARAMETER Digest;
  2637. USER_CREDENTIALS UserCreds;
  2638. PUCHAR pucAuthData = NULL;
  2639. PUCHAR pucExpandedAuthData = NULL;
  2640. ULONG ulAuthDataSize = 0;
  2641. ULONG ulExpandedAuthDataSize = 0;
  2642. BOOL fDigestValid = FALSE;
  2643. BOOL fForwardedRequest = FALSE;
  2644. USHORT indx = 0;
  2645. USHORT indxmax = 0;
  2646. SAMPR_HANDLE UserHandle = NULL;
  2647. DebugLog((DEB_TRACE_FUNC, "DigestResponseBru: Entering\n"));
  2648. ZeroMemory(&UserCreds, sizeof(USER_CREDENTIALS));
  2649. ASSERT(pMessageRequest);
  2650. ASSERT(pulResponse);
  2651. ASSERT(ppResponse);
  2652. // Initialize the outputs
  2653. *pulResponse = 0;
  2654. *ppResponse = NULL;
  2655. Status = DigestInit(&Digest);
  2656. if (!NT_SUCCESS(Status))
  2657. {
  2658. DebugLog((DEB_ERROR, "DigestResponseBru: Failed to initialize digest struct\n"));
  2659. goto CleanUp;
  2660. }
  2661. Status = BlobDecodeRequest(cbMessageRequest, pMessageRequest, &Digest);
  2662. if (!NT_SUCCESS (Status))
  2663. {
  2664. DebugLog((DEB_ERROR, "DigestResponseBru: Failed to copy over SessionKey 0x%x\n", Status));
  2665. goto CleanUp;
  2666. }
  2667. #if DBG
  2668. (void)DigestPrint(&Digest);
  2669. #endif
  2670. // Verify that only md5-sess requests will be processed. Do not allow md5 requests to be verified
  2671. // since the H(a1) from MD5 is returned to server which could be used in future md5-sess requests without
  2672. // client permission
  2673. if (Digest.typeAlgorithm != MD5_SESS)
  2674. {
  2675. Status = SEC_E_QOP_NOT_SUPPORTED;
  2676. DebugLog((DEB_ERROR, "DigestResponseBru: Only MD5-sess algorithm can be processed 0x%x\n", Status));
  2677. goto CleanUp;
  2678. }
  2679. // If there is a domain associated with this cracked username, check to see if it matches the current domain
  2680. // if it does not match, this request will need to be forwarded to the correct DC for the user's account
  2681. if (Digest.ustrCrackedDomain.Length)
  2682. {
  2683. BOOL fIsThisDomain = FALSE;
  2684. fIsThisDomain = DigestCompareDomainNames(&Digest.ustrCrackedDomain,
  2685. &(g_NtDigestSecPkg.DnsDomainName),
  2686. &(g_NtDigestSecPkg.DomainName));
  2687. // Check if need to forward the request to another domain where user's account is located
  2688. if (!fIsThisDomain)
  2689. {
  2690. DebugLog((DEB_WARN, "DigestResponseBru: Request is not for this domain - forward request\n"));
  2691. Status = DigestForwardRequest(&Digest,
  2692. &fDigestValid,
  2693. &ulAuthDataSize,
  2694. &pucAuthData);
  2695. if ( !NT_SUCCESS( Status ) )
  2696. {
  2697. DebugLog((DEB_ERROR, "DigestResponseBru: ForwardRequest Failed Status = 0x%x\n", Status));
  2698. goto CleanUp;
  2699. }
  2700. else
  2701. {
  2702. DebugLog((DEB_TRACE, "DigestResponseBru: ForwardRequest Succeeded Status = 0x%x\n", Status));
  2703. fForwardedRequest = TRUE;
  2704. goto Generate_Response;
  2705. }
  2706. }
  2707. }
  2708. // Pull out the username and domain to process
  2709. Status = UserCredentialsExtract(&Digest, &UserCreds);
  2710. if (!NT_SUCCESS (Status))
  2711. {
  2712. DebugLog((DEB_ERROR, "DigestResponseBru: Failed UserCredentialsExtract 0x%x\n", Status));
  2713. goto CleanUp;
  2714. }
  2715. // Now attempt to locate the user account in this DC
  2716. Status = DigestOpenSamUser(&Digest, &UserHandle, &pucAuthData, &ulAuthDataSize);
  2717. if ( !NT_SUCCESS( Status ) )
  2718. {
  2719. DebugLog((DEB_WARN, "DigestResponseBru: DigestOpenSamUser failed for user %wZ, Status = 0x%x\n",
  2720. &(Digest.ustrUsername), Status));
  2721. // Check if we should try a crackname and call to other DC
  2722. if ((Status == STATUS_NO_SUCH_USER) && (Digest.usFlags & FLAG_CRACKNAME_ON_DC))
  2723. {
  2724. DebugLog((DEB_WARN, "DigestResponseBru: Call Crackname and Redirect as necessary\n"));
  2725. Status = DigestForwardRequest(&Digest,
  2726. &fDigestValid,
  2727. &ulAuthDataSize,
  2728. &pucAuthData);
  2729. if ( !NT_SUCCESS( Status ) )
  2730. {
  2731. DebugLog((DEB_ERROR, "DigestResponseBru: Failed crackname for user %wZ, Status = 0x%x\n",
  2732. &(Digest.ustrUsername), Status));
  2733. goto CleanUp;
  2734. }
  2735. else
  2736. {
  2737. DebugLog((DEB_TRACE, "DigestResponseBru: Succeeded crackname for user %wZ, Status = 0x%x\n",
  2738. &(Digest.ustrUsername), Status));
  2739. fForwardedRequest = TRUE;
  2740. goto Generate_Response;
  2741. }
  2742. }
  2743. DebugLog((DEB_ERROR, "DigestResponseBru: Failed local open SAM for user %wZ, Status = 0x%x\n",
  2744. &(Digest.ustrUsername), Status));
  2745. goto CleanUp;
  2746. }
  2747. else
  2748. {
  2749. DebugLog((DEB_TRACE, "DigestResponseBru: DigestOpenSamUser Succeeded AuthData size %ld\n", ulAuthDataSize ));
  2750. }
  2751. // Extract Passwords (Cleartext and hash if available) - select which ones to utilize
  2752. Status = DigestGetPasswd(UserHandle, &Digest, &UserCreds);
  2753. if (!NT_SUCCESS(Status))
  2754. {
  2755. DebugLog((DEB_ERROR, "DigestResponseBru: Failed to find password status 0x%x\n", Status));
  2756. goto CleanUp;
  2757. }
  2758. DebugLog((DEB_TRACE, "DigestResponseBru: Got password valid %d\n", UserCreds.fIsValidPasswd));
  2759. DebugLog((DEB_TRACE, "DigestResponseBru: HashCred size is %d\n", UserCreds.strDigestHash.Length ));
  2760. // We now have passwd - either/both pre-computed hash or passwd
  2761. // Also, an authData blob to marshal back to server
  2762. // Now validate the Digest ChallengeResponse
  2763. // Check precalculated hashes first
  2764. fDigestValid = FALSE;
  2765. if (UserCreds.fIsValidDigestHash == TRUE)
  2766. {
  2767. // Need to cycle over the possible matching hashes based on username format
  2768. indx = 1; // skip the first hash this is the header
  2769. // Determine max number of hashes to inspect
  2770. indxmax = (((USHORT)NUMPRECALC_HEADERS < UserCreds.usDigestHashCnt) ? (USHORT)NUMPRECALC_HEADERS : UserCreds.usDigestHashCnt);
  2771. while ((fDigestValid == FALSE) && (indx <= indxmax))
  2772. {
  2773. if (UserCreds.sHashTags[indx])
  2774. {
  2775. DebugLog((DEB_TRACE, "DigestResponseBru: Checking Precalc hash 0x%x\n", indx));
  2776. UserCreds.wHashSelected = indx;
  2777. }
  2778. else
  2779. {
  2780. indx++; // skip to the next hash since incorrect format
  2781. continue;
  2782. }
  2783. StringFree(&Digest.strSessionKey); // clear out any previous session key info
  2784. StatusSub = DigestCalculation(&Digest, &UserCreds);
  2785. if (NT_SUCCESS(StatusSub))
  2786. { // Precalculated hash matched!
  2787. DebugLog((DEB_TRACE, "DigestResponseBru: Digest valid with precalc hash 0x%x\n", indx));
  2788. fDigestValid = TRUE;
  2789. }
  2790. else if ((StatusSub == STATUS_WRONG_PASSWORD) || (StatusSub == SEC_E_NO_CREDENTIALS))
  2791. { // Really we know only that the Hash did not compare - could be anything incorrect
  2792. // We do not provide information that the password was incorrect
  2793. DebugLog((DEB_TRACE, "DigestResponseBru: Digest did not match precalc hash 0x%x\n", indx));
  2794. indx++;
  2795. }
  2796. else
  2797. {
  2798. Status = StatusSub;
  2799. DebugLog((DEB_ERROR, "DigestResponseBru: Digest Verify Failed 0x%x\n", Status));
  2800. goto CleanUp;
  2801. }
  2802. }
  2803. if (fDigestValid == FALSE)
  2804. {
  2805. UserCreds.fIsValidDigestHash = FALSE; // no need to try to use any of these hashes again
  2806. }
  2807. }
  2808. // If ClearText passwd available, then try to validate the Digest ChallengeResponse
  2809. if ((fDigestValid == FALSE) && (UserCreds.fIsValidPasswd == TRUE))
  2810. {
  2811. StringFree(&Digest.strSessionKey); // clear out any previous session key info
  2812. StatusSub = DigestCalculation(&Digest, &UserCreds);
  2813. if (NT_SUCCESS(StatusSub))
  2814. { // Really we know only that the Hash did not compare - could be anything incorrect
  2815. // We do not provide information that the password was incorrect
  2816. DebugLog((DEB_TRACE, "DigestResponseBru: Digest valid with cleartext password\n"));
  2817. fDigestValid = TRUE;
  2818. }
  2819. else if (StatusSub == STATUS_WRONG_PASSWORD)
  2820. { // Really we know only that the Hash did not compare - could be anything incorrect
  2821. // We do not provide information that the password was incorrect
  2822. DebugLog((DEB_WARN, "DigestResponseBru: Digest did not match cleartext passsword\n"));
  2823. }
  2824. else
  2825. {
  2826. Status = StatusSub;
  2827. DebugLog((DEB_ERROR, "DigestResponseBru: Digest Verify Failed 0x%x\n", Status));
  2828. goto CleanUp;
  2829. }
  2830. }
  2831. // Adjust badpassword counter for user based on SubStatus
  2832. // for either STATUS_SUCCESS (fDigestValid must be TRUE) or STATUS_WRONG_PASSWORD
  2833. // do not process other system errors (such as no pre-calc hashes yet and no cleartext password)
  2834. if ((fDigestValid == TRUE) || (StatusSub == STATUS_WRONG_PASSWORD))
  2835. {
  2836. SAM_LOGON_STATISTICS LogonStats;
  2837. RtlZeroMemory(&LogonStats, sizeof(LogonStats));
  2838. if (fDigestValid == TRUE)
  2839. {
  2840. LogonStats.StatisticsToApply = USER_LOGON_NET_SUCCESS_LOGON;
  2841. DebugLog((DEB_TRACE, "DigestResponseBru: Audit logging Successful logon\n"));
  2842. }
  2843. else if (StatusSub == STATUS_WRONG_PASSWORD)
  2844. {
  2845. LogonStats.StatisticsToApply = USER_LOGON_BAD_PASSWORD | USER_LOGON_BAD_PASSWORD_WKSTA;
  2846. (void)UnicodeStringReference(&LogonStats.Workstation, &Digest.ustrWorkstation);
  2847. DebugLog((DEB_TRACE, "DigestResponseBru: Audit logging Bad Password\n"));
  2848. }
  2849. if ( LogonStats.StatisticsToApply != 0 ) {
  2850. NTSTATUS LogonStatus;
  2851. // LogonStats.StatisticsToApply |= USER_LOGON_TYPE_NTLM;
  2852. LogonStatus = DigestUpdateLogonStatistics(
  2853. UserHandle,
  2854. &LogonStats );
  2855. }
  2856. }
  2857. Generate_Response:
  2858. // Update any group memberships of the user with the server's domain info
  2859. if ((fDigestValid == TRUE) &&
  2860. (Digest.usFlags & FLAG_SERVERS_DOMAIN) &&
  2861. ulAuthDataSize &&
  2862. pucAuthData)
  2863. {
  2864. if (fForwardedRequest == TRUE)
  2865. {
  2866. DebugLog((DEB_TRACE, "DigestResponseBru: Filtering the SIDs from forwarded domain\n"));
  2867. Status = DigestCheckPacForSidFiltering(&Digest, &pucAuthData, &ulAuthDataSize);
  2868. if (!NT_SUCCESS(Status))
  2869. {
  2870. DebugLog((DEB_ERROR, "DigestResponseBru: Failed DigestCheckPacForSidFiltering 0x%x\n", Status));
  2871. goto CleanUp;
  2872. }
  2873. }
  2874. DebugLog((DEB_TRACE, "DigestResponseBru: Calling ExpandAuthDataforDomain AuthDataSize %ld\n", ulAuthDataSize));
  2875. Status = g_LsaFunctions->ExpandAuthDataForDomain(pucAuthData,
  2876. ulAuthDataSize,
  2877. NULL,
  2878. &pucExpandedAuthData,
  2879. &ulExpandedAuthDataSize);
  2880. if (!NT_SUCCESS(Status))
  2881. {
  2882. DebugLog((DEB_ERROR, "DigestResponseBru: Failed ExpandAuthDataforDomain 0x%x\n", Status));
  2883. goto CleanUp;
  2884. }
  2885. DebugLog((DEB_TRACE, "DigestResponseBru: ExpandAuthDataforDomain Expanded AuthDataSize %ld\n", ulExpandedAuthDataSize));
  2886. // Replace auth data with Expanded AuthData
  2887. if (pucAuthData)
  2888. {
  2889. g_LsaFunctions->FreeLsaHeap(pucAuthData);
  2890. }
  2891. pucAuthData = pucExpandedAuthData;
  2892. ulAuthDataSize = ulExpandedAuthDataSize;
  2893. ulExpandedAuthDataSize = 0;
  2894. pucExpandedAuthData = NULL;
  2895. }
  2896. #ifdef ROGUE_DC
  2897. DebugLog((DEB_TRACE, "DigestResponseBru: Calling DigestInstrumentRoguePac\n"));
  2898. Status = DigestInstrumentRoguePac(&pucAuthData, &ulAuthDataSize);
  2899. if (!NT_SUCCESS(Status))
  2900. {
  2901. DebugLog((DEB_ERROR, "DigestResponseBru: Failed DigestInstrumentRoguePac 0x%x\n", Status));
  2902. goto CleanUp;
  2903. }
  2904. #endif
  2905. Status = DigestEncodeResponse(fDigestValid,
  2906. &Digest,
  2907. ulAuthDataSize,
  2908. pucAuthData,
  2909. pulResponse,
  2910. ppResponse);
  2911. if (!NT_SUCCESS(Status))
  2912. {
  2913. DebugLog((DEB_ERROR, "DigestResponseBru: Failed DigestEncodeResponse 0x%x\n", Status));
  2914. goto CleanUp;
  2915. }
  2916. CleanUp:
  2917. DigestFree(&Digest);
  2918. UserCredentialsFree(&UserCreds);
  2919. // Cleanup any allocated heap from User's AuthData
  2920. if (pucAuthData)
  2921. {
  2922. g_LsaFunctions->FreeLsaHeap(pucAuthData);
  2923. pucAuthData = NULL;
  2924. ulAuthDataSize = 0;
  2925. }
  2926. if (pucExpandedAuthData)
  2927. {
  2928. g_LsaFunctions->FreeLsaHeap(pucExpandedAuthData);
  2929. pucExpandedAuthData = NULL;
  2930. ulExpandedAuthDataSize = 0;
  2931. }
  2932. if (UserHandle)
  2933. {
  2934. StatusSub = DigestCloseSamUser(UserHandle);
  2935. if (!NT_SUCCESS(StatusSub))
  2936. {
  2937. DebugLog((DEB_ERROR,"DigestResponseBru: failed DigestCloseSamUser 0x%x\n", StatusSub));
  2938. }
  2939. UserHandle = NULL;
  2940. }
  2941. DebugLog((DEB_TRACE_FUNC, "DigestResponseBru: Leaving Status 0x%x\n", Status));
  2942. return(Status);
  2943. }
  2944. NTSTATUS
  2945. ContextPrint(PDIGEST_CONTEXT pContext)
  2946. {
  2947. NTSTATUS Status = STATUS_SUCCESS;
  2948. if (!pContext)
  2949. {
  2950. return (STATUS_INVALID_PARAMETER);
  2951. }
  2952. if (pContext->typeDigest == DIGEST_UNDEFINED)
  2953. {
  2954. DebugLog((DEB_TRACE, "Context: DIGEST_UNDEFINED\n"));
  2955. }
  2956. if (pContext->typeDigest == NO_DIGEST_SPECIFIED)
  2957. {
  2958. DebugLog((DEB_ERROR, "Context: NO_DIGEST_SPECIFIED\n"));
  2959. }
  2960. if (pContext->typeDigest == DIGEST_CLIENT)
  2961. {
  2962. DebugLog((DEB_TRACE, "Context: DIGEST_CLIENT\n"));
  2963. }
  2964. if (pContext->typeDigest == DIGEST_SERVER)
  2965. {
  2966. DebugLog((DEB_TRACE, "Context: DIGEST_SERVER\n"));
  2967. }
  2968. if (pContext->typeDigest == SASL_SERVER)
  2969. {
  2970. DebugLog((DEB_TRACE, "Context: SASL_SERVER\n"));
  2971. }
  2972. if (pContext->typeDigest == SASL_CLIENT)
  2973. {
  2974. DebugLog((DEB_TRACE, "Context: SASL_CLIENT\n"));
  2975. }
  2976. if (pContext->typeQOP == QOP_UNDEFINED)
  2977. {
  2978. DebugLog((DEB_ERROR, "Context: QOP: QOP_UNDEFINED\n"));
  2979. }
  2980. if (pContext->typeQOP == NO_QOP_SPECIFIED)
  2981. {
  2982. DebugLog((DEB_TRACE, "Context: QOP: NO_QOP_SPECIFIED\n"));
  2983. }
  2984. if (pContext->typeQOP == AUTH)
  2985. {
  2986. DebugLog((DEB_TRACE, "Context: QOP: AUTH\n"));
  2987. }
  2988. if (pContext->typeQOP == AUTH_INT)
  2989. {
  2990. DebugLog((DEB_TRACE, "Context: QOP: AUTH_INT\n"));
  2991. }
  2992. if (pContext->typeQOP == AUTH_CONF)
  2993. {
  2994. DebugLog((DEB_TRACE, "Context: QOP: AUTH_CONF\n"));
  2995. }
  2996. if (pContext->typeAlgorithm == ALGORITHM_UNDEFINED)
  2997. {
  2998. DebugLog((DEB_ERROR, "Context: Algorithm: ALGORITHM_UNDEFINED\n"));
  2999. }
  3000. if (pContext->typeAlgorithm == NO_ALGORITHM_SPECIFIED)
  3001. {
  3002. DebugLog((DEB_TRACE, "Context: Algorithm: NO_ALGORITHM_SPECIFIED\n"));
  3003. }
  3004. if (pContext->typeAlgorithm == MD5)
  3005. {
  3006. DebugLog((DEB_TRACE, "Context: Algorithm: MD5\n"));
  3007. }
  3008. if (pContext->typeAlgorithm == MD5_SESS)
  3009. {
  3010. DebugLog((DEB_TRACE, "Context: Algorithm: MD5_SESS\n"));
  3011. }
  3012. if (pContext->typeCipher == CIPHER_RC4)
  3013. {
  3014. DebugLog((DEB_TRACE, "Context: Cipher: RC4\n"));
  3015. }
  3016. if (pContext->typeCipher == CIPHER_RC4_40)
  3017. {
  3018. DebugLog((DEB_TRACE, "Context: Cipher: RC4_40\n"));
  3019. }
  3020. if (pContext->typeCipher == CIPHER_RC4_56)
  3021. {
  3022. DebugLog((DEB_TRACE, "Context: Cipher: RC4_56\n"));
  3023. }
  3024. if (pContext->typeCipher == CIPHER_3DES)
  3025. {
  3026. DebugLog((DEB_TRACE, "Context: Cipher: 3DES\n"));
  3027. }
  3028. if (pContext->typeCipher == CIPHER_DES)
  3029. {
  3030. DebugLog((DEB_TRACE, "Context: Cipher: DES\n"));
  3031. }
  3032. if (pContext->typeCharset == ISO_8859_1)
  3033. {
  3034. DebugLog((DEB_TRACE, "Context: Charset: ISO-8859-1\n"));
  3035. }
  3036. if (pContext->typeCharset == UTF_8)
  3037. {
  3038. DebugLog((DEB_TRACE, "Context: Charset: UTF-8\n"));
  3039. }
  3040. DebugLog((DEB_TRACE, "Context: NC %d\n", pContext->ulNC));
  3041. DebugLog((DEB_TRACE, "Context: LogonId (%x:%lx)\n", pContext->LoginID.HighPart, pContext->LoginID.LowPart ));
  3042. DebugLog((DEB_TRACE, "Context: strNonce %Z\n", &(pContext->strNonce)));
  3043. DebugLog((DEB_TRACE, "Context: strCNonce %Z\n", &(pContext->strCNonce)));
  3044. DebugLog((DEB_TRACE, "Context: strOpaque %Z\n", &(pContext->strOpaque)));
  3045. DebugLog((DEB_TRACE, "Context: strSessionKey %Z\n", &(pContext->strSessionKey)));
  3046. DebugLog((DEB_TRACE, "Context: ustrDomain %wZ\n", &(pContext->ustrDomain)));
  3047. DebugLog((DEB_TRACE, "Context: ustrAccountName %wZ\n", &(pContext->ustrAccountName)));
  3048. DebugLog((DEB_TRACE, "Context: SendMaxBuf %lu\n", &(pContext->ulSendMaxBuf)));
  3049. return(Status);
  3050. }
  3051. // Extracts the username and domain from the digest directives
  3052. // Need to process the character set to properly decode the directive values
  3053. // The major character sets are UTF-8 and ISO-8859-1
  3054. // The forms that may be present in the directive values are:
  3055. // Username Realm
  3056. // 1. username domain
  3057. // 2. domain/username NULL
  3058. // 3. UPN NULL
  3059. NTSTATUS UserCredentialsExtract(PDIGEST_PARAMETER pDigest,
  3060. PUSER_CREDENTIALS pUserCreds)
  3061. {
  3062. NTSTATUS Status = STATUS_SUCCESS;
  3063. DebugLog((DEB_TRACE_FUNC, "UserCredentialsExtract: Entering\n"));
  3064. if (!pDigest || !(pDigest->refstrParam[MD5_AUTH_USERNAME].Length))
  3065. {
  3066. Status = STATUS_NO_SUCH_USER;
  3067. DebugLog((DEB_ERROR, "UserCredentialsExtract: Invalid Username or realm\n"));
  3068. goto CleanUp;
  3069. }
  3070. Status = DigestDecodeDirectiveStrings(pDigest);
  3071. if (!NT_SUCCESS (Status))
  3072. {
  3073. DebugLog((DEB_ERROR, "UserCredentialsExtract: DigestDecodeDirectiveStrings error 0x%x\n", Status));
  3074. goto CleanUp;
  3075. }
  3076. // Values to use for the username and realm in hash calculations
  3077. Status = UnicodeStringDuplicate(&(pUserCreds->ustrUsername), &(pDigest->ustrUsername));
  3078. if (!NT_SUCCESS (Status))
  3079. {
  3080. DebugLog((DEB_ERROR, "UserCredentialsExtract: UnicodeStringDuplicate Username error 0x%x\n", Status));
  3081. goto CleanUp;
  3082. }
  3083. Status = UnicodeStringDuplicate(&(pUserCreds->ustrRealm), &(pDigest->ustrRealm));
  3084. if (!NT_SUCCESS (Status))
  3085. {
  3086. DebugLog((DEB_ERROR, "UserCredentialsExtract: UnicodeStringDuplicate Domain error 0x%x\n", Status));
  3087. goto CleanUp;
  3088. }
  3089. CleanUp:
  3090. DebugLog((DEB_TRACE_FUNC, "UserCredentialsExtract: Leaving Status 0x%x\n", Status));
  3091. return(Status);
  3092. }
  3093. // Release memory allocated into UserCredentials
  3094. NTSTATUS UserCredentialsFree(PUSER_CREDENTIALS pUserCreds)
  3095. {
  3096. NTSTATUS Status = STATUS_SUCCESS;
  3097. UnicodeStringFree(&(pUserCreds->ustrUsername));
  3098. if (pUserCreds->ustrPasswd.MaximumLength)
  3099. {
  3100. SecureZeroMemory(pUserCreds->ustrPasswd.Buffer, pUserCreds->ustrPasswd.MaximumLength);
  3101. }
  3102. UnicodeStringFree(&(pUserCreds->ustrPasswd));
  3103. UnicodeStringFree(&(pUserCreds->ustrRealm));
  3104. if (pUserCreds->strDigestHash.MaximumLength)
  3105. {
  3106. SecureZeroMemory(pUserCreds->strDigestHash.Buffer, pUserCreds->strDigestHash.MaximumLength);
  3107. }
  3108. StringFree(&(pUserCreds->strDigestHash));
  3109. return(Status);
  3110. }
  3111. //+--------------------------------------------------------------------
  3112. //
  3113. // Function: DigestSASLResponseAuth
  3114. //
  3115. // Synopsis: Generate the ResponseAuth from the server
  3116. //
  3117. // Arguments: pDigest - pointer to Digest parameter struct
  3118. // pOutputToken - location to send output string to
  3119. //
  3120. // Returns: NTSTATUS
  3121. //
  3122. // Notes:
  3123. //
  3124. //---------------------------------------------------------------------
  3125. NTSTATUS DigestSASLResponseAuth(
  3126. IN PDIGEST_PARAMETER pDigest,
  3127. OUT PSecBuffer pOutputToken)
  3128. {
  3129. NTSTATUS Status = STATUS_SUCCESS;
  3130. ULONG cbLenNeeded = 0;
  3131. STRING strReqAuth = {0};
  3132. PCHAR pczTemp = NULL;
  3133. ASSERT(pDigest);
  3134. DebugLog((DEB_TRACE_FUNC, "DigestSASLResponseAuth: Entering\n"));
  3135. Status = DigestCalculateResponseAuth(pDigest, &strReqAuth);
  3136. if (!NT_SUCCESS(Status))
  3137. {
  3138. DebugLog((DEB_ERROR, "DigestSASLResponseAuth: Request Auth failed : 0x%x\n", Status));
  3139. goto CleanUp;
  3140. }
  3141. cbLenNeeded = sizeof(RSPAUTH_STR);
  3142. cbLenNeeded += strReqAuth.Length;
  3143. // allocate the buffers for output - in the future can optimze to allocate exact amount needed
  3144. pczTemp = (PCHAR)DigestAllocateMemory(cbLenNeeded + 1);
  3145. if (!pczTemp)
  3146. {
  3147. DebugLog((DEB_ERROR, "ContextCreateChal: No memory for output buffers\n"));
  3148. goto CleanUp;
  3149. }
  3150. sprintf(pczTemp, RSPAUTH_STR, &strReqAuth);
  3151. pOutputToken->cbBuffer = (USHORT)strlen(pczTemp);
  3152. pOutputToken->pvBuffer = pczTemp;
  3153. CleanUp:
  3154. DebugLog((DEB_TRACE_FUNC, "DigestSASLResponseAuth: Leaving 0x%x\n", Status));
  3155. StringFree(&strReqAuth);
  3156. return Status;
  3157. }
  3158. //+--------------------------------------------------------------------
  3159. //
  3160. // Function: DigestCalculateResponseAuth
  3161. //
  3162. // Synopsis: Calculate the ResponseAuth Hash value
  3163. //
  3164. // Arguments: pDigest - pointer to Digest parameter struct
  3165. // pCoutputToken - location to send output string to
  3166. //
  3167. // Returns: NTSTATUS
  3168. //
  3169. // Notes:
  3170. //
  3171. //---------------------------------------------------------------------
  3172. NTSTATUS DigestCalculateResponseAuth(
  3173. IN PDIGEST_PARAMETER pDigest,
  3174. OUT PSTRING pstrHash)
  3175. {
  3176. NTSTATUS Status = STATUS_SUCCESS;
  3177. STRING strHA2 = {0};
  3178. STRING strcQOP = {0};
  3179. ASSERT(pDigest);
  3180. ASSERT(pstrHash);
  3181. DebugLog((DEB_TRACE_FUNC, "DigestCalculateResponseAuth: Entering\n"));
  3182. DigestPrint(pDigest);
  3183. StringFree(pstrHash);
  3184. // Establish which QOP utilized
  3185. if (pDigest->typeQOP == AUTH_CONF)
  3186. {
  3187. RtlInitString(&strcQOP, AUTHCONFSTR);
  3188. }
  3189. else if (pDigest->typeQOP == AUTH_INT)
  3190. {
  3191. RtlInitString(&strcQOP, AUTHINTSTR);
  3192. }
  3193. else if (pDigest->typeQOP == AUTH)
  3194. {
  3195. RtlInitString(&strcQOP, AUTHSTR);
  3196. }
  3197. else
  3198. {
  3199. RtlInitString(&strcQOP, NULL);
  3200. }
  3201. // Calculate H(A2)
  3202. // For QOP unspecified or "auth" H(A2) = H( : URI)
  3203. // For QOP Auth-int or Auth-conf H(A2) = H( : URI: H(entity-body))
  3204. if ((pDigest->typeQOP == AUTH) || (pDigest->typeQOP == NO_QOP_SPECIFIED))
  3205. {
  3206. // Unspecified or Auth
  3207. DebugLog((DEB_TRACE, "DigestCalculateResponseAuth: H(A2) using AUTH/Unspecified\n"));
  3208. Status = DigestHash7(NULL,
  3209. &(pDigest->refstrParam[MD5_AUTH_URI]),
  3210. NULL, NULL, NULL, NULL, NULL,
  3211. TRUE, &strHA2);
  3212. if (!NT_SUCCESS(Status))
  3213. {
  3214. DebugLog((DEB_ERROR, "DigestCalculateResponseAuthDigestCalculateResponseAuth: H(A2) failed : 0x%x\n", Status));
  3215. goto CleanUp;
  3216. }
  3217. }
  3218. else
  3219. {
  3220. // Auth-int or Auth-conf
  3221. DebugLog((DEB_TRACE, "DigestCalculateResponseAuth: H(A2) using AUTH-INT/CONF\n"));
  3222. Status = DigestHash7(NULL,
  3223. &(pDigest->refstrParam[MD5_AUTH_URI]),
  3224. &(pDigest->refstrParam[MD5_AUTH_HENTITY]),
  3225. NULL, NULL, NULL, NULL,
  3226. TRUE, &strHA2);
  3227. if (!NT_SUCCESS(Status))
  3228. {
  3229. DebugLog((DEB_ERROR, "DigestCalculateResponseAuth H(A2) auth-int failed : 0x%x\n", Status));
  3230. goto CleanUp;
  3231. }
  3232. }
  3233. // We now have calculated H(A2)
  3234. // Calculate Request-Digest
  3235. // For QOP of Auth, Auth-int, Auth-conf Req-Digest = H( H(A1): nonce: nc: cnonce: qop: H(A2))
  3236. if ((pDigest->typeQOP == AUTH) || (pDigest->typeQOP == AUTH_INT) || (pDigest->typeQOP == AUTH_CONF))
  3237. {
  3238. Status = DigestHash7(&(pDigest->strSessionKey),
  3239. &(pDigest->refstrParam[MD5_AUTH_NONCE]),
  3240. &(pDigest->refstrParam[MD5_AUTH_NC]),
  3241. &(pDigest->refstrParam[MD5_AUTH_CNONCE]),
  3242. &strcQOP,
  3243. &strHA2, NULL,
  3244. TRUE, pstrHash);
  3245. if (!NT_SUCCESS(Status))
  3246. {
  3247. DebugLog((DEB_ERROR, "DigestCalculateResponseAuth: Request Auth failed : 0x%x\n", Status));
  3248. goto CleanUp;
  3249. }
  3250. }
  3251. else if (pDigest->typeQOP == NO_QOP_SPECIFIED)
  3252. {
  3253. DebugLog((DEB_TRACE, "DigestCalculateResponseAuth: Response calc using Unspecified QOP\n"));
  3254. Status = DigestHash7(&(pDigest->strSessionKey),
  3255. &(pDigest->refstrParam[MD5_AUTH_NONCE]),
  3256. &strHA2,
  3257. NULL,
  3258. NULL,
  3259. NULL, NULL,
  3260. TRUE, pstrHash);
  3261. if (!NT_SUCCESS(Status))
  3262. {
  3263. DebugLog((DEB_ERROR, "DigestCalculateResponseAuth: Request Auth failed : 0x%x\n", Status));
  3264. goto CleanUp;
  3265. }
  3266. }
  3267. else
  3268. {
  3269. Status = STATUS_INVALID_PARAMETER;
  3270. DebugLog((DEB_ERROR, "DigestCalculateResponseAuth: Unknown Auth Type: 0x%x\n", Status));
  3271. goto CleanUp;
  3272. }
  3273. DebugLog((DEB_TRACE, "DigestCalculateResponseAuth: ResponseAuth is %Z\n", pstrHash));
  3274. CleanUp:
  3275. DebugLog((DEB_TRACE_FUNC, "DigestCalculateResponseAuth: Leaving 0x%x\n", Status));
  3276. StringFree(&strHA2);
  3277. return Status;
  3278. }
  3279. //+--------------------------------------------------------------------
  3280. //
  3281. // Function: DigestDecodeUserAccount
  3282. //
  3283. // Synopsis: Process the Digest to extract Account Username, Account Domain
  3284. // generic passthrough domain controller, and index for precalculated digest hash
  3285. //
  3286. // Arguments: pDigest - pointer to Digest parameter struct
  3287. //
  3288. // Returns: NTSTATUS
  3289. //
  3290. // Notes:
  3291. //
  3292. //---------------------------------------------------------------------
  3293. NTSTATUS DigestDecodeUserAccount(
  3294. IN PDIGEST_PARAMETER pDigest)
  3295. {
  3296. NTSTATUS Status = STATUS_SUCCESS;
  3297. PWCHAR pwczAcct = NULL;
  3298. USHORT uNumWChar = 0;
  3299. DebugLog((DEB_TRACE_FUNC, "DigestDecodeUserAccount: Entering\n"));
  3300. DebugLog((DEB_TRACE, "DigestDecodeUserAccount: Checking format on username %wZ\n", &pDigest->ustrUsername));
  3301. if (pDigest->ustrUsername.Length / sizeof(WCHAR) > UNLEN)
  3302. {
  3303. Status = STATUS_INVALID_PARAMETER;
  3304. DebugLog((DEB_ERROR, "DigestDecodeUserAccount: Username too long 0x%x\n", Status));
  3305. goto CleanUp;
  3306. }
  3307. // On the server, we can only safely check the username for NetBIOS format. We can not crack UPNs locally.
  3308. DebugLog((DEB_TRACE, "DigestDecodeUserAccount: Checking for NetBios Format\n"));
  3309. if (pDigest->ustrUsername.Length)
  3310. {
  3311. pwczAcct = wcschr(pDigest->ustrUsername.Buffer, L'\\');
  3312. if (pwczAcct)
  3313. {
  3314. DebugLog((DEB_TRACE, "DigestDecodeUserAccount: DS_NT4_ACCOUNT_NAME Succeeded\n"));
  3315. // Output name format always will be domain+'\'+account+'\0'
  3316. // Need account location
  3317. UnicodeStringFree(&(pDigest->ustrCrackedAccountName)); // clear for retry decode
  3318. Status = UnicodeStringWCharDuplicate(&(pDigest->ustrCrackedAccountName), pwczAcct+1, 0);
  3319. if (!NT_SUCCESS (Status))
  3320. {
  3321. DebugLog((DEB_ERROR, "DigestDecodeUserAccount: UnicodeStringDuplicate Username error 0x%x\n", Status));
  3322. goto CleanUp;
  3323. }
  3324. UnicodeStringFree(&(pDigest->ustrCrackedDomain)); // clear for retry decode
  3325. uNumWChar = (USHORT)(pwczAcct - pDigest->ustrUsername.Buffer);
  3326. if (uNumWChar > 1)
  3327. { // we will only copyout the Domain if there is aleast 1 WChar; otherwise it is of the form "\accountname"
  3328. Status = UnicodeStringWCharDuplicate(&(pDigest->ustrCrackedDomain), pDigest->ustrUsername.Buffer, uNumWChar);
  3329. if (!NT_SUCCESS (Status))
  3330. {
  3331. DebugLog((DEB_ERROR, "DigestDecodeUserAccount: UnicodeStringDuplicate Domain error 0x%x\n", Status));
  3332. goto CleanUp;
  3333. }
  3334. }
  3335. pDigest->typeName = NAMEFORMAT_NETBIOS;
  3336. Status = STATUS_SUCCESS;
  3337. goto CleanUp;
  3338. }
  3339. // Not a NetBIOS format - might be SAMAccount or UPN
  3340. pDigest->usFlags |= FLAG_CRACKNAME_ON_DC; // we could not crack account name locally - do on DC
  3341. pDigest->typeName = NAMEFORMAT_UNKNOWN; // We do not know what format this is (could be UPN)
  3342. Status = STATUS_SUCCESS;
  3343. goto CleanUp;
  3344. }
  3345. Status = STATUS_INVALID_ADDRESS;
  3346. DebugLog((DEB_ERROR, "DigestDecodeUserAccount: Invalid format for username and realm\n", Status));
  3347. CleanUp:
  3348. DebugLog((DEB_TRACE_FUNC, "DigestDecodeUserAccount: Leaving 0x%x\n", Status));
  3349. return Status;
  3350. }
  3351. //+--------------------------------------------------------------------
  3352. //
  3353. // Function: DigestDirectiveCheck
  3354. //
  3355. // Synopsis: Check the directives parsed out of Challenge or ChalengeResponse
  3356. // sinple checks if directives are present
  3357. //
  3358. // Arguments: pDigest - pointer to Digest parameter struct
  3359. // typeDigestMode - SASL or HTTP mode
  3360. //
  3361. // Returns: NTSTATUS
  3362. //
  3363. // Notes:
  3364. //
  3365. //---------------------------------------------------------------------
  3366. NTSTATUS
  3367. DigestDirectiveCheck(
  3368. IN PDIGEST_PARAMETER pDigest,
  3369. IN DIGEST_TYPE typeDigest)
  3370. {
  3371. NTSTATUS Status = STATUS_SUCCESS;
  3372. ASSERT(pDigest);
  3373. DebugLog((DEB_TRACE_FUNC, "DigestDirectiveCheck: Entering\n"));
  3374. if (pDigest->usDirectiveCnt[MD5_AUTH_USERNAME] > 1)
  3375. {
  3376. Status = SEC_E_INVALID_TOKEN;
  3377. DebugLog((DEB_ERROR, "DigestDirectiveCheck: Username directive error 0x%x\n", Status));
  3378. goto CleanUp;
  3379. }
  3380. if (pDigest->usDirectiveCnt[MD5_AUTH_NONCE] != 1)
  3381. {
  3382. Status = SEC_E_INVALID_TOKEN;
  3383. DebugLog((DEB_ERROR, "DigestDirectiveCheck: Nonce directives error 0x%x\n", Status));
  3384. goto CleanUp;
  3385. }
  3386. if (pDigest->usDirectiveCnt[MD5_AUTH_STALE] > 1)
  3387. {
  3388. Status = SEC_E_INVALID_TOKEN;
  3389. DebugLog((DEB_ERROR, "DigestDirectiveCheck: Stale directives error 0x%x\n", Status));
  3390. goto CleanUp;
  3391. }
  3392. if (pDigest->usDirectiveCnt[MD5_AUTH_MAXBUF] > 1)
  3393. {
  3394. Status = SEC_E_INVALID_TOKEN;
  3395. DebugLog((DEB_ERROR, "DigestDirectiveCheck: MaxBuf directives error 0x%x\n", Status));
  3396. goto CleanUp;
  3397. }
  3398. if (pDigest->usDirectiveCnt[MD5_AUTH_CHARSET] > 1)
  3399. {
  3400. Status = SEC_E_INVALID_TOKEN;
  3401. DebugLog((DEB_ERROR, "DigestDirectiveCheck: Charset directives error 0x%x\n", Status));
  3402. goto CleanUp;
  3403. }
  3404. if ((typeDigest == SASL_CLIENT) && (pDigest->usDirectiveCnt[MD5_AUTH_ALGORITHM] != 1))
  3405. {
  3406. Status = SEC_E_INVALID_TOKEN;
  3407. DebugLog((DEB_ERROR, "DigestDirectiveCheck: Algorithm directives error 0x%x\n", Status));
  3408. goto CleanUp;
  3409. }
  3410. // From RFC, realm must be present if server provided one in challenge
  3411. // Since we are the server processing the challengeResonse and we always send
  3412. // a realm, require a realm to be specified.
  3413. if ((typeDigest == SASL_SERVER) && (pDigest->usDirectiveCnt[MD5_AUTH_REALM] != 1))
  3414. {
  3415. Status = SEC_E_INVALID_TOKEN;
  3416. DebugLog((DEB_ERROR, "DigestDirectiveCheck: Sasl server requires one realm 0x%x\n", Status));
  3417. goto CleanUp;
  3418. }
  3419. if (pDigest->usDirectiveCnt[MD5_AUTH_CIPHER] > 1)
  3420. {
  3421. Status = SEC_E_INVALID_TOKEN;
  3422. DebugLog((DEB_ERROR, "DigestDirectiveCheck: Algorithm directives error 0x%x\n", Status));
  3423. goto CleanUp;
  3424. }
  3425. if (pDigest->usDirectiveCnt[MD5_AUTH_NC] > 1)
  3426. {
  3427. Status = SEC_E_INVALID_TOKEN;
  3428. DebugLog((DEB_ERROR, "DigestDirectiveCheck: NC directives error 0x%x\n", Status));
  3429. goto CleanUp;
  3430. }
  3431. // Set flags according to directives present
  3432. if (pDigest->usDirectiveCnt[MD5_AUTH_AUTHZID])
  3433. {
  3434. pDigest->usFlags |= FLAG_AUTHZID_PROVIDED;
  3435. }
  3436. CleanUp:
  3437. DebugLog((DEB_TRACE_FUNC, "DigestDirectiveCheck: Leaving 0x%x\n", Status));
  3438. return Status;
  3439. }
  3440. //+--------------------------------------------------------------------
  3441. //
  3442. // Function: DigestForwardRequest
  3443. //
  3444. // Synopsis: The AccountName provided can not be processed locally.
  3445. // The user's account is not local no this domain. CrackName will be called if necessary to identify
  3446. // another DC (which should have that user account)
  3447. // The output Response will be directly passed back to the originating server
  3448. //
  3449. // Arguments:
  3450. // OUT PBOOL pfDigestValid - was the digest request call successful
  3451. // OUT PULONG ulAuthDataSize - holds the size of the AuthData returned from the forwarded call
  3452. // OUT PUCHAR pucAuthDat - allocated buffer to hold the AuthData (if any) for the digest request
  3453. //
  3454. // Returns: NTSTATUS
  3455. //
  3456. // Notes: After the crack name, the Digest_Parameters could be reformed with the newly discovered
  3457. // CrackNameAccount and CrackNameDomain and then set the NameType field.
  3458. //
  3459. //---------------------------------------------------------------------
  3460. NTSTATUS
  3461. DigestForwardRequest(
  3462. IN PDIGEST_PARAMETER pDigest,
  3463. OUT PBOOL pfDigestValid,
  3464. OUT PULONG pulAuthDataSize,
  3465. OUT PUCHAR *ppucAuthData)
  3466. {
  3467. NTSTATUS Status = STATUS_SUCCESS;
  3468. NTSTATUS SubStatus = STATUS_SUCCESS;
  3469. ULONG RequestSize = 0;
  3470. ULONG ResponseSize = 0;
  3471. PUCHAR Where = NULL;
  3472. PWCHAR pwczAcct = NULL;
  3473. USHORT usFlagsSaved = 0;
  3474. BOOL fWithinForest = FALSE;
  3475. BOOL fIsThisDomain = FALSE;
  3476. PWCHAR pwszName = NULL; // name for the account to crack
  3477. UNICODE_STRING MsvPackageName = CONSTANT_UNICODE_STRING(TEXT(MSV1_0_PACKAGE_NAME));
  3478. PUNICODE_STRING pusrtForwardDomain = NULL;
  3479. WCHAR wczCrackedDnsDomain[DNS_MAX_NAME_LENGTH + 1];
  3480. DWORD dwCrackedDnsDomainCnt = sizeof( wczCrackedDnsDomain ) / sizeof( WCHAR );
  3481. WCHAR wczCrackedName[UNLEN+DNS_MAX_NAME_LENGTH + 2];
  3482. DWORD dwCrackedNameCnt = sizeof( wczCrackedName ) / sizeof( WCHAR );
  3483. DWORD dwCrackError = 0;
  3484. USHORT cbDigestParamEncoded = 0; // Contains the number of bytes in Request to send out
  3485. BYTE *pDigestParamEncoded = NULL;
  3486. PMSV1_0_PASSTHROUGH_REQUEST PassthroughRequest = NULL;
  3487. PMSV1_0_PASSTHROUGH_RESPONSE PassthroughResponse = NULL;
  3488. ASSERT(pDigest);
  3489. ASSERT(pfDigestValid);
  3490. ASSERT(pulAuthDataSize);
  3491. ASSERT(ppucAuthData);
  3492. // Set default output
  3493. *pfDigestValid = FALSE;
  3494. *pulAuthDataSize = 0;
  3495. *ppucAuthData = NULL;
  3496. DebugLog((DEB_TRACE_FUNC, "DigestForwardRequest: Entering\n"));
  3497. // Verify that this request is being sent from the server's domain
  3498. if (!(pDigest->usFlags & FLAG_SERVERS_DOMAIN))
  3499. {
  3500. Status = STATUS_CURRENT_DOMAIN_NOT_ALLOWED;
  3501. DebugLog((DEB_ERROR,"DigestForwardRequest: Forwarding request allowed only from Server's Domain\n"));
  3502. goto CleanUp;
  3503. }
  3504. // If no CrackedDomain specified, call CrackName on the GC
  3505. // for the AccountName and domain to utilize
  3506. // If format was NT4 NetBIOS, then userCrackedDomain and userCrackedName already filled in
  3507. // If not, then try to crackname a UPN,
  3508. // if crackname fails, then use "hint" of realm to go there for SamAccountName
  3509. if (!pDigest->ustrCrackedDomain.Length)
  3510. {
  3511. DebugLog((DEB_ERROR,"DigestForwardRequest: Cracking the name to determine which DC to forward request to\n"));
  3512. if (pDigest->ustrUsername.Length && pDigest->ustrUsername.Buffer)
  3513. {
  3514. pwszName = (PWCHAR)DigestAllocateMemory(pDigest->ustrUsername.Length + sizeof(WCHAR));
  3515. if (!pwszName)
  3516. {
  3517. Status = SEC_E_INSUFFICIENT_MEMORY;
  3518. DebugLog((DEB_ERROR,"DigestForwardRequest: Out of memory for accountname\n"));
  3519. goto CleanUp;
  3520. }
  3521. RtlCopyMemory(
  3522. pwszName,
  3523. pDigest->ustrUsername.Buffer,
  3524. pDigest->ustrUsername.Length
  3525. );
  3526. }
  3527. DebugLog((DEB_TRACE, "DigestForwardRequest: Checking with CrackSingleName\n"));
  3528. Status = CrackSingleName(DS_USER_PRINCIPAL_NAME,
  3529. DS_NAME_NO_FLAGS,
  3530. pwszName,
  3531. DS_NT4_ACCOUNT_NAME,
  3532. &dwCrackedDnsDomainCnt,
  3533. wczCrackedDnsDomain,
  3534. &dwCrackedNameCnt,
  3535. wczCrackedName,
  3536. &dwCrackError);
  3537. if (NT_SUCCESS(Status) && (DS_NAME_NO_ERROR == dwCrackError))
  3538. {
  3539. DebugLog((DEB_TRACE, "DigestForwardRequest: CrackSingleName DS_USER_PRINCIPAL_NAME Succeeded\n"));
  3540. DebugLog((DEB_TRACE, "DigestForwardRequest: CrackSingleName dwErr 0x%x CrackName %S CrackDomain %S\n",
  3541. dwCrackError,
  3542. wczCrackedName,
  3543. wczCrackedDnsDomain));
  3544. // Output name format always will be domain+'\'+account+'\0'
  3545. // Need account location
  3546. pwczAcct = wcschr(wczCrackedName, L'\\');
  3547. if (!pwczAcct)
  3548. {
  3549. Status = STATUS_INVALID_ADDRESS;
  3550. DebugLog((DEB_ERROR, "DigestForwardRequest: Can not locate Account name 0x%x\n", Status));
  3551. goto CleanUp;
  3552. }
  3553. UnicodeStringFree(&(pDigest->ustrCrackedAccountName));
  3554. UnicodeStringFree(&(pDigest->ustrCrackedDomain));
  3555. Status = UnicodeStringWCharDuplicate(&(pDigest->ustrCrackedAccountName), pwczAcct+1,0);
  3556. if (!NT_SUCCESS (Status))
  3557. {
  3558. DebugLog((DEB_ERROR, "DigestForwardRequest: UnicodeStringDuplicate Username error 0x%x\n", Status));
  3559. goto CleanUp;
  3560. }
  3561. Status = UnicodeStringWCharDuplicate(&(pDigest->ustrCrackedDomain), wczCrackedDnsDomain,0);
  3562. if (!NT_SUCCESS (Status))
  3563. {
  3564. DebugLog((DEB_ERROR, "DigestForwardRequest: UnicodeStringDuplicate Domain error 0x%x\n", Status));
  3565. goto CleanUp;
  3566. }
  3567. pDigest->typeName = NAMEFORMAT_UPN;
  3568. pDigest->usFlags = pDigest->usFlags & (~FLAG_CRACKNAME_ON_DC); // reset - name is now cracked
  3569. pusrtForwardDomain = &(pDigest->ustrCrackedDomain); // forward request to this domain
  3570. }
  3571. else
  3572. {
  3573. // Failed to CrackName a UPN, check if realm "hint" specified, if so use that
  3574. if (pDigest->ustrRealm.Length)
  3575. {
  3576. // if realm is not the same as this domain (we should have been able to open the sam then)
  3577. fIsThisDomain = DigestCompareDomainNames(&(pDigest->ustrRealm),
  3578. &(g_NtDigestSecPkg.DnsDomainName),
  3579. &(g_NtDigestSecPkg.DomainName));
  3580. if (fIsThisDomain)
  3581. {
  3582. DebugLog((DEB_TRACE, "DigestForwardRequest: Realm hint provides no forwarding domain\n"));
  3583. Status = STATUS_LOGON_FAILURE; // indicate that there was a format problem
  3584. goto CleanUp;
  3585. }
  3586. pDigest->typeName = NAMEFORMAT_UNKNOWN; // We do not know if a local UPN or SAMAccount
  3587. pusrtForwardDomain = &(pDigest->ustrRealm); // forward request to realm's "hint" domain
  3588. }
  3589. else
  3590. {
  3591. // Can not crackname and there is no realm "hint" so give up
  3592. DebugLog((DEB_TRACE, "DigestForwardRequest: CrackSingleName DS_USER_PRINCIPAL_NAME Failed 0x%x CrackErr 0x%x\n",
  3593. Status,
  3594. dwCrackError));
  3595. if (NT_SUCCESS(Status))
  3596. {
  3597. Status = STATUS_LOGON_FAILURE; // indicate that there was a format problem
  3598. }
  3599. goto CleanUp;
  3600. }
  3601. }
  3602. }
  3603. else
  3604. {
  3605. pusrtForwardDomain = &(pDigest->ustrCrackedDomain); // we already have a cracked domain to forward request to
  3606. }
  3607. // Verify that we have a domain to forward request to
  3608. if (!pusrtForwardDomain || (!pusrtForwardDomain->Length))
  3609. {
  3610. DebugLog((DEB_TRACE, "DigestForwardRequest: failed to determine DC to forward request to\n"));
  3611. Status = STATUS_NO_SUCH_DOMAIN;
  3612. goto CleanUp;
  3613. }
  3614. // Verify that the target DC is not an external trust - allow only passthrough within forest - bug 506114
  3615. Status = LsaIIsDomainWithinForest(pusrtForwardDomain,
  3616. &fWithinForest,
  3617. NULL,
  3618. &(pDigest->pTrustSid),
  3619. NULL, // &(pDigest->ulTrustDirection),
  3620. NULL, // &(pDigest->ulTrustType),
  3621. NULL); // &(pDigest->ulTrustAttributes));
  3622. if (!NT_SUCCESS (Status))
  3623. {
  3624. DebugLog((DEB_ERROR, "DigestCrackNameAndForward: LsaIIsDomainWithinForest failed 0x%x\n", Status));
  3625. goto CleanUp;
  3626. }
  3627. if (!fWithinForest)
  3628. {
  3629. Status = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  3630. DebugLog((DEB_ERROR, "DigestCrackNameAndForward: Target DC is outside forest - fail request 0x%x\n", Status));
  3631. goto CleanUp;
  3632. }
  3633. // Since the service domain and the user account domain are in the same forest
  3634. // the following parameters can be fixed
  3635. // Note: this means that WDigest does not honor quarantined domains!!!!!!
  3636. // To enable quarantined domains, we need to have callout processing at each domain hop - which NTLM
  3637. // generic passthrough does not support FIXFIX
  3638. pDigest->ulTrustType = TRUST_TYPE_UPLEVEL;
  3639. pDigest->ulTrustDirection = TRUST_DIRECTION_OUTBOUND;
  3640. pDigest->ulTrustAttributes = 0;
  3641. // Re-encode the blob to send over to the other DC
  3642. // Serialize the Digest Parameters
  3643. // Clear the server's domain flag so only a single hop from this domain is allowed
  3644. usFlagsSaved = pDigest->usFlags;
  3645. pDigest->usFlags = pDigest->usFlags & (~FLAG_SERVERS_DOMAIN);
  3646. cbDigestParamEncoded = 0; // Will be allocated by BlobEncodeRequest
  3647. Status = BlobEncodeRequest(pDigest, &pDigestParamEncoded, &cbDigestParamEncoded);
  3648. if (!NT_SUCCESS (Status))
  3649. {
  3650. DebugLog((DEB_ERROR, "DigestForwardRequest: BlobEncodeRequest failed 0x%x\n", Status));
  3651. goto CleanUp;
  3652. }
  3653. // Restore the original flags for this digest request
  3654. pDigest->usFlags = usFlagsSaved;
  3655. // Callpackge over to that domain
  3656. RequestSize = sizeof(MSV1_0_PASSTHROUGH_REQUEST) +
  3657. pusrtForwardDomain->Length +
  3658. g_ustrNtDigestPackageName.Length +
  3659. cbDigestParamEncoded;
  3660. PassthroughRequest = (PMSV1_0_PASSTHROUGH_REQUEST) DigestAllocateMemory(RequestSize);
  3661. if (PassthroughRequest == NULL)
  3662. {
  3663. Status = STATUS_INSUFFICIENT_RESOURCES;
  3664. goto CleanUp;
  3665. }
  3666. Where = (PUCHAR) (PassthroughRequest + 1); // Points to byte AFTER passthrough header
  3667. PassthroughRequest->MessageType = MsV1_0GenericPassthrough;
  3668. PassthroughRequest->DomainName.Length = pusrtForwardDomain->Length;
  3669. PassthroughRequest->DomainName.MaximumLength = pusrtForwardDomain->Length;
  3670. PassthroughRequest->DomainName.Buffer = (LPWSTR) Where; // this is NOT NULL TERMINATED
  3671. RtlCopyMemory(
  3672. Where,
  3673. pusrtForwardDomain->Buffer,
  3674. pusrtForwardDomain->Length
  3675. );
  3676. Where += pusrtForwardDomain->Length;
  3677. PassthroughRequest->PackageName.Length = g_ustrNtDigestPackageName.Length;
  3678. PassthroughRequest->PackageName.MaximumLength = g_ustrNtDigestPackageName.Length;
  3679. PassthroughRequest->PackageName.Buffer = (LPWSTR) Where; // Not NULL terminated - relative reference
  3680. RtlCopyMemory(
  3681. Where,
  3682. g_ustrNtDigestPackageName.Buffer,
  3683. g_ustrNtDigestPackageName.Length
  3684. );
  3685. Where += g_ustrNtDigestPackageName.Length;
  3686. PassthroughRequest->LogonData = Where;
  3687. PassthroughRequest->DataLength = (ULONG)cbDigestParamEncoded;
  3688. RtlCopyMemory(
  3689. Where,
  3690. pDigestParamEncoded,
  3691. cbDigestParamEncoded
  3692. );
  3693. //
  3694. // We've build the buffer, now call NTLM to pass it through.
  3695. //
  3696. DebugLog((DEB_TRACE, "DigestForwardRequest: Sending request via passthrough to %wZ\n", pusrtForwardDomain));
  3697. Status = g_LsaFunctions->CallPackage(
  3698. &MsvPackageName,
  3699. PassthroughRequest,
  3700. RequestSize, // How many bytes to send in Request
  3701. (PVOID *) &PassthroughResponse, // Place the buffers here
  3702. &ResponseSize, // Passed back the size of the buffer
  3703. &SubStatus // Return code from Digest Auth on the DC
  3704. );
  3705. DebugLog((DEB_TRACE, "DigestForwardRequest: Server passthrough Responsesize %ld, Status 0x%x, SubStatus 0x%x\n",
  3706. ResponseSize, Status, SubStatus));
  3707. if (!NT_SUCCESS(Status))
  3708. {
  3709. DebugLog((DEB_ERROR,"DigestForwardRequest: Failed to call MSV package to verify Digest: 0x%x\n",Status));
  3710. if (Status == STATUS_INVALID_INFO_CLASS)
  3711. {
  3712. Status = STATUS_LOGON_FAILURE;
  3713. }
  3714. goto CleanUp;
  3715. }
  3716. // Now check the status of the request over on the remote DC
  3717. if (!NT_SUCCESS(SubStatus))
  3718. {
  3719. Status = SubStatus;
  3720. DebugLog((DEB_ERROR,"DigestForwardRequest: DC failed to verify Digest Response: 0x%x\n",Status));
  3721. goto CleanUp;
  3722. }
  3723. if (!PassthroughResponse || (ResponseSize == 0) || (PassthroughResponse->DataLength == 0))
  3724. {
  3725. DebugLog((DEB_ERROR,"DigestForwardRequest: PassthroughResponse contains no valid data\n"));
  3726. Status = STATUS_INTERNAL_ERROR;
  3727. goto CleanUp;
  3728. }
  3729. DebugLog((DEB_TRACE, "DigestForwardRequest: PassthroughResponse->DataLength %ld\n",
  3730. PassthroughResponse->DataLength));
  3731. Where = (PUCHAR) (PassthroughResponse + 1); // start copy after header
  3732. Status = DigestDecodeResponse(PassthroughResponse->DataLength,
  3733. Where,
  3734. pfDigestValid,
  3735. pulAuthDataSize,
  3736. ppucAuthData,
  3737. &(pDigest->strSessionKey),
  3738. &(pDigest->ustrCrackedAccountName),
  3739. &(pDigest->ustrCrackedDomain));
  3740. if (!NT_SUCCESS(Status))
  3741. {
  3742. DebugLog((DEB_ERROR,"DigestForwardRequest: Failed DigestDecodeResponse 0x%x\n",Status));
  3743. goto CleanUp;
  3744. }
  3745. CleanUp:
  3746. BlobFreeRequest(pDigestParamEncoded);
  3747. pDigestParamEncoded = NULL;
  3748. cbDigestParamEncoded = 0;
  3749. if (pwszName != NULL)
  3750. {
  3751. DigestFreeMemory(pwszName);
  3752. }
  3753. if (PassthroughRequest != NULL)
  3754. {
  3755. DigestFreeMemory(PassthroughRequest);
  3756. }
  3757. if (PassthroughResponse != NULL)
  3758. {
  3759. g_LsaFunctions->FreeReturnBuffer(PassthroughResponse);
  3760. }
  3761. DebugLog((DEB_TRACE_FUNC, "DigestForwardRequest: Leaving 0x%x\n", Status));
  3762. return Status;
  3763. }
  3764. /*++
  3765. Routine Description:
  3766. This routine parses a Token Descriptor and pulls out the useful
  3767. information.
  3768. Arguments:
  3769. TokenDescriptor - Descriptor of the buffer containing the token.
  3770. BufferIndex - Selects which buffer to extract
  3771. Token - Handle to the SecBuffer to write selected buffer to.
  3772. ReadonlyOK - TRUE if the token buffer may be readonly.
  3773. Return Value:
  3774. TRUE - If token buffer was properly found.
  3775. --*/
  3776. BOOLEAN
  3777. SspGetTokenBufferByIndex(
  3778. IN PSecBufferDesc TokenDescriptor,
  3779. IN ULONG BufferIndex,
  3780. OUT PSecBuffer * Token,
  3781. IN BOOLEAN ReadonlyOK
  3782. )
  3783. {
  3784. NTSTATUS StatusTmp = STATUS_SUCCESS;
  3785. PSecBuffer Buffer = NULL;
  3786. //
  3787. // Verify inputs are valid
  3788. //
  3789. ASSERT(*Token != NULL);
  3790. ASSERT(TokenDescriptor);
  3791. if ( !ARGUMENT_PRESENT( TokenDescriptor) ) {
  3792. return FALSE;
  3793. }
  3794. if (TokenDescriptor->ulVersion != SECBUFFER_VERSION)
  3795. {
  3796. DebugLog((DEB_ERROR,"SspGetTokenBufferByIndex: Wrong Version number\n"));
  3797. return FALSE;
  3798. }
  3799. //
  3800. // Verify that it is a valid location
  3801. //
  3802. if (BufferIndex >= TokenDescriptor->cBuffers)
  3803. {
  3804. DebugLog((DEB_ERROR,"SspGetTokenBufferByIndex: Index out of range for SecBufferDesc\n"));
  3805. return FALSE;
  3806. }
  3807. // DebugLog((DEB_TRACE,"SspGetTokenBufferByIndex: NumberTokens %d\n",TokenDescriptor->cBuffers));
  3808. Buffer = &TokenDescriptor->pBuffers[BufferIndex];
  3809. //
  3810. // If the buffer is readonly and readonly isn't OK,
  3811. // reject the buffer.
  3812. //
  3813. if (!ReadonlyOK && (Buffer->BufferType & SECBUFFER_READONLY))
  3814. {
  3815. DebugLog((DEB_TRACE,"SspGetTokenBufferByIndex: request write on READONLY Token buffer\n"));
  3816. return FALSE;
  3817. }
  3818. //
  3819. // Return the requested information
  3820. //
  3821. if (Buffer->cbBuffer && Buffer->pvBuffer)
  3822. {
  3823. StatusTmp = g_LsaFunctions->MapBuffer(Buffer, Buffer);
  3824. if (!NT_SUCCESS(StatusTmp))
  3825. {
  3826. DebugLog((DEB_ERROR,"SspGetTokenBufferByIndex: Unable to MapBuffer 0x%x\n", StatusTmp));
  3827. return FALSE;
  3828. }
  3829. }
  3830. *Token = Buffer;
  3831. return TRUE;
  3832. }
  3833. //+--------------------------------------------------------------------
  3834. //
  3835. // Function: DigestEncodeResponse
  3836. //
  3837. // Synopsis: Generate the serialized response from the digest processing to send back along generic passthrough
  3838. //
  3839. // Arguments:
  3840. //
  3841. // Returns: NTSTATUS
  3842. //
  3843. //
  3844. //---------------------------------------------------------------------
  3845. NTSTATUS
  3846. DigestEncodeResponse(IN BOOL fDigestValid,
  3847. IN PDIGEST_PARAMETER pDigest,
  3848. IN ULONG ulAuthDataSize,
  3849. IN PUCHAR pucAuthData,
  3850. OUT PULONG pulResponse,
  3851. OUT PBYTE *ppResponse)
  3852. {
  3853. NTSTATUS Status = STATUS_SUCCESS;
  3854. USHORT usAcctNameSize = 0;
  3855. PCHAR pcLoc = NULL;
  3856. ULONG ulBuffer = 0;
  3857. PDIGEST_BLOB_RESPONSE pBlobResponse = NULL;
  3858. DebugLog((DEB_TRACE_FUNC, "DigestEncodeResponse: Entering\n"));
  3859. ASSERT(pDigest);
  3860. ASSERT(pulResponse);
  3861. ASSERT(ppResponse);
  3862. *pulResponse = 0;
  3863. *ppResponse = NULL;
  3864. // domain\accountname NULL format
  3865. usAcctNameSize = pDigest->ustrCrackedDomain.Length + pDigest->ustrCrackedAccountName.Length + (2 * sizeof(WCHAR));
  3866. // We completed the Auth (it might have failed though)
  3867. // Make sure enough room in output buffer
  3868. ulBuffer = sizeof(DIGEST_BLOB_RESPONSE);
  3869. if (fDigestValid == TRUE)
  3870. {
  3871. // We succeeded in auth so send back AuthData for tokens & AccountName
  3872. ulBuffer += ulAuthDataSize;
  3873. ulBuffer = ulBuffer + usAcctNameSize;
  3874. }
  3875. else
  3876. {
  3877. ulAuthDataSize = 0; // Do not send back Auth data unless Digest Calc Succeeded
  3878. Status = STATUS_LOGON_FAILURE; // report back only Logon failuer UnknownUser/BadPassword
  3879. goto CleanUp;
  3880. }
  3881. DebugLog((DEB_TRACE, "DigestEncodeResponse: Total size for return buffer is %ld bytes\n", ulBuffer));
  3882. pBlobResponse = (PDIGEST_BLOB_RESPONSE)DigestAllocateMemory(ulBuffer);
  3883. if (!pBlobResponse)
  3884. {
  3885. Status = SEC_E_INSUFFICIENT_MEMORY;
  3886. DebugLog((DEB_ERROR, "DigestEncodeResponse: Can not allocate memory for Output Response Buffer\n"));
  3887. goto CleanUp;
  3888. }
  3889. pBlobResponse->MessageType = VERIFY_DIGEST_MESSAGE_RESPONSE;
  3890. pBlobResponse->version = DIGEST_BLOB_VERSION;
  3891. if (fDigestValid == TRUE)
  3892. {
  3893. pBlobResponse->Status = STATUS_SUCCESS;
  3894. }
  3895. else
  3896. {
  3897. pBlobResponse->Status = STATUS_LOGON_FAILURE;
  3898. }
  3899. pBlobResponse->ulAuthDataSize = ulAuthDataSize;
  3900. pBlobResponse->usAcctNameSize = usAcctNameSize; // size includes a NULL terminator
  3901. pBlobResponse->ulBlobSize = ulBuffer;
  3902. // Could be an assert too
  3903. if (pDigest->strSessionKey.Length != MD5_HASH_HEX_SIZE)
  3904. {
  3905. DebugLog((DEB_ERROR, "DigestEncodeResponse: Failed SessionKey generation\n"));
  3906. Status = STATUS_INTERNAL_ERROR; // Program flow failure
  3907. goto CleanUp;
  3908. }
  3909. pBlobResponse->SessionKeyMaxLength = MD5_HASH_HEX_SIZE + 1; // MD5 hash + NULL
  3910. memcpy(pBlobResponse->SessionKey, pDigest->strSessionKey.Buffer, MD5_HASH_HEX_SIZE);
  3911. pcLoc = &(pBlobResponse->cAuthData);
  3912. if (ulAuthDataSize)
  3913. { // Copy over the ExpandedAuthData only if DigestCalc succeeded (i.e. ulExpandedAuthDataSize != 0)
  3914. pcLoc = &(pBlobResponse->cAuthData);
  3915. memcpy(pcLoc, pucAuthData, ulAuthDataSize);
  3916. pcLoc = pcLoc + ulAuthDataSize;
  3917. }
  3918. // Copy over the accountname for use in querycontextattributes
  3919. memcpy(pcLoc, pDigest->ustrCrackedDomain.Buffer, pDigest->ustrCrackedDomain.Length);
  3920. pcLoc = pcLoc + pDigest->ustrCrackedDomain.Length;
  3921. memcpy(pcLoc, L"\\", sizeof(WCHAR));
  3922. pcLoc = pcLoc + sizeof(WCHAR);
  3923. memcpy(pcLoc, pDigest->ustrCrackedAccountName.Buffer, pDigest->ustrCrackedAccountName.Length);
  3924. pcLoc = pcLoc + pDigest->ustrCrackedAccountName.Length + sizeof(WCHAR); // skip the NULL WCHAR
  3925. // OK we are done filling in output Response buffer
  3926. *pulResponse = ulBuffer; // Set the size of the response blob
  3927. *ppResponse = (PBYTE)pBlobResponse; // set the buffer allocated
  3928. pBlobResponse = NULL;
  3929. CleanUp:
  3930. if (!NT_SUCCESS(Status))
  3931. {
  3932. if (pBlobResponse)
  3933. {
  3934. DigestFreeMemory(pBlobResponse);
  3935. pBlobResponse = NULL;
  3936. }
  3937. }
  3938. DebugLog((DEB_TRACE_FUNC, "DigestEncodeResponse: Leaving Status 0x%x\n", Status));
  3939. return(Status);
  3940. }
  3941. //+--------------------------------------------------------------------
  3942. //
  3943. // Function: DigestDecodeResponse
  3944. //
  3945. // Synopsis: Process the serialized response from Passthrough for forwarded digest's response
  3946. //
  3947. // Arguments:
  3948. //
  3949. // Returns: NTSTATUS
  3950. //
  3951. //
  3952. //---------------------------------------------------------------------
  3953. NTSTATUS
  3954. DigestDecodeResponse(IN ULONG ulResponseDataSize,
  3955. IN PUCHAR puResponseData,
  3956. OUT PBOOL pfDigestValid,
  3957. OUT PULONG pulAuthDataSize,
  3958. OUT PUCHAR *ppucAuthData,
  3959. OUT PSTRING pstrSessionKey,
  3960. OUT OPTIONAL PUNICODE_STRING pustrAccountName,
  3961. OUT OPTIONAL PUNICODE_STRING pustrAccountDomain
  3962. )
  3963. {
  3964. NTSTATUS Status = STATUS_SUCCESS;
  3965. PDIGEST_BLOB_RESPONSE pDigestResponse = NULL;
  3966. BOOL fKnownFormat = FALSE;
  3967. USHORT usNumWChar = 0;
  3968. PUCHAR puTempAuth = NULL;
  3969. USHORT uNumWChar = 0;
  3970. UNICODE_STRING ustrAccount = {0};
  3971. PUSHORT pwczAcct = NULL;
  3972. ASSERT(pfDigestValid);
  3973. ASSERT(ppucAuthData);
  3974. ASSERT(pulAuthDataSize);
  3975. ASSERT(pstrSessionKey);
  3976. DebugLog((DEB_TRACE_FUNC, "DigestDecodeResponse: Entering\n"));
  3977. *pfDigestValid = FALSE;
  3978. *pulAuthDataSize = 0;
  3979. *ppucAuthData = NULL;
  3980. StringFree(pstrSessionKey);
  3981. // Now pull out info from the Passthrough Response structure
  3982. if (ulResponseDataSize < sizeof(DIGEST_BLOB_RESPONSE))
  3983. {
  3984. // The returned data is not the expected size
  3985. Status = STATUS_INTERNAL_ERROR;
  3986. DebugLog((DEB_ERROR,"DigestDecodeResponse: DC Response wrong data size: 0x%x\n",Status));
  3987. goto CleanUp;
  3988. }
  3989. // Copy it to a structure - can do direct map once we know this works OK
  3990. // Copy to Allocated memory forces aligment of fields
  3991. pDigestResponse = (PDIGEST_BLOB_RESPONSE)DigestAllocateMemory(ulResponseDataSize);
  3992. if (!pDigestResponse)
  3993. {
  3994. Status = SEC_E_INSUFFICIENT_MEMORY;
  3995. DebugLog((DEB_ERROR,"DigestDecodeResponse: Out of memory for response buffer alloc\n"));
  3996. goto CleanUp;
  3997. }
  3998. RtlCopyMemory(
  3999. pDigestResponse,
  4000. puResponseData,
  4001. ulResponseDataSize
  4002. );
  4003. // We should now have all the data we need for sessionkeys and if verified auth
  4004. // Check the MessageType and Versions if supported
  4005. if ((pDigestResponse->MessageType == VERIFY_DIGEST_MESSAGE_RESPONSE) && (pDigestResponse->version == DIGEST_BLOB_VERSION))
  4006. {
  4007. fKnownFormat = TRUE; // We know how to process this blob from the DC
  4008. DebugLog((DEB_TRACE,"DigestDecodeResponse: DC Response known type and version\n"));
  4009. }
  4010. if (!fKnownFormat)
  4011. {
  4012. // The returned data not of a known type or version
  4013. Status = STATUS_INTERNAL_ERROR;
  4014. DebugLog((DEB_ERROR,"DigestDecodeResponse: DC Response unknown type or version\n"));
  4015. goto CleanUp;
  4016. }
  4017. DebugLog((DEB_TRACE,"DigestDecodeResponse: Processing DC Response\n"));
  4018. if (!pDigestResponse->ulAuthDataSize)
  4019. {
  4020. // We do not have any AuthData
  4021. Status = STATUS_INTERNAL_ERROR;
  4022. DebugLog((DEB_ERROR, "DigestDecodeResponse: Failed to determine AuthData\n"));
  4023. goto CleanUp;
  4024. }
  4025. // Copy over data to place on correct boundary (alloc should force long word boundary)
  4026. puTempAuth = (PUCHAR)DigestAllocateMemory( ROUND_UP_COUNT(pDigestResponse->ulAuthDataSize, 8));
  4027. if (!puTempAuth)
  4028. {
  4029. Status = SEC_E_INSUFFICIENT_MEMORY;
  4030. DebugLog((DEB_ERROR, "DigestDecodeResponse: out of memory on response PAC buffer\n"));
  4031. goto CleanUp;
  4032. }
  4033. memcpy(puTempAuth,&(pDigestResponse->cAuthData),pDigestResponse->ulAuthDataSize);
  4034. // Copy out the SessionKey
  4035. StringFree(pstrSessionKey);
  4036. Status = StringAllocate(pstrSessionKey, pDigestResponse->SessionKeyMaxLength);
  4037. if (!NT_SUCCESS(Status))
  4038. {
  4039. DebugLog((DEB_ERROR, "DigestDecodeResponse: Failed to alloc Sessionkey memory\n"));
  4040. goto CleanUp;
  4041. }
  4042. memcpy(pstrSessionKey->Buffer, pDigestResponse->SessionKey, pDigestResponse->SessionKeyMaxLength);
  4043. pstrSessionKey->Length = (USHORT)strlencounted(pstrSessionKey->Buffer, pDigestResponse->SessionKeyMaxLength);
  4044. DebugLog((DEB_TRACE, "DigestDecodeResponse: Response Data from passthrough call\n"));
  4045. DebugLog((DEB_TRACE, " Session Key: %Z\n", pstrSessionKey));
  4046. // Pull out the NetBios Accountname and domain - optional output
  4047. if (pustrAccountName || pustrAccountDomain)
  4048. {
  4049. UnicodeStringFree(pustrAccountName);
  4050. UnicodeStringFree(pustrAccountDomain);
  4051. if (pDigestResponse->usAcctNameSize > sizeof(WCHAR))
  4052. {
  4053. usNumWChar = pDigestResponse->usAcctNameSize / sizeof(WCHAR);
  4054. Status = UnicodeStringAllocate(&ustrAccount, usNumWChar);
  4055. if (!NT_SUCCESS(Status))
  4056. {
  4057. DebugLog((DEB_ERROR, "DigestDecodeResponse: Failed to alloc Account memory\n"));
  4058. goto CleanUp;
  4059. }
  4060. RtlCopyMemory(ustrAccount.Buffer,
  4061. &(pDigestResponse->cAuthData) + pDigestResponse->ulAuthDataSize,
  4062. pDigestResponse->usAcctNameSize);
  4063. ustrAccount.Length = ustrlencounted((const short *)ustrAccount.Buffer, pDigestResponse->usAcctNameSize) *
  4064. sizeof(WCHAR);
  4065. DebugLog((DEB_TRACE, "DigestDecodeResponse: NetBios AccountName %wZ\n", &ustrAccount));
  4066. }
  4067. // Now extract the accountname and the domain from the NETBios format domain\username
  4068. pwczAcct = wcschr(ustrAccount.Buffer, L'\\');
  4069. if (pwczAcct)
  4070. {
  4071. // Output name format always will be domain+'\'+account+'\0'
  4072. // Need account location
  4073. if (pustrAccountName)
  4074. {
  4075. Status = UnicodeStringWCharDuplicate(pustrAccountName, pwczAcct+1, 0);
  4076. if (!NT_SUCCESS (Status))
  4077. {
  4078. DebugLog((DEB_ERROR, "DigestDecodeResponse: UnicodeStringDuplicate Account error 0x%x\n", Status));
  4079. goto CleanUp;
  4080. }
  4081. }
  4082. if (pustrAccountDomain)
  4083. {
  4084. uNumWChar = (USHORT)(pwczAcct - ustrAccount.Buffer);
  4085. if (uNumWChar > 1)
  4086. { // we will only copyout the Domain if there is aleast 1 WChar; otherwise it is of the form "\accountname"
  4087. Status = UnicodeStringWCharDuplicate(pustrAccountDomain, ustrAccount.Buffer, uNumWChar);
  4088. if (!NT_SUCCESS (Status))
  4089. {
  4090. DebugLog((DEB_ERROR, "DigestDecodeResponse: UnicodeStringDuplicate Domain error 0x%x\n", Status));
  4091. goto CleanUp;
  4092. }
  4093. }
  4094. }
  4095. }
  4096. DebugLog((DEB_TRACE, "DigestDecodeResponse: Account %wZ Domain %wZ\n",
  4097. pustrAccountName, pustrAccountDomain));
  4098. }
  4099. DebugLog((DEB_TRACE, "DigestDecodeResponse: AuthData Size %lu\n", pDigestResponse->ulAuthDataSize));
  4100. *pulAuthDataSize = pDigestResponse->ulAuthDataSize;
  4101. *ppucAuthData = puTempAuth; // sign over the memory
  4102. puTempAuth = NULL;
  4103. *pfDigestValid = TRUE;
  4104. CleanUp:
  4105. if (puTempAuth)
  4106. { // Clear out temp allocation if function failed
  4107. DigestFreeMemory(puTempAuth);
  4108. puTempAuth = NULL;
  4109. }
  4110. UnicodeStringFree(&ustrAccount);
  4111. if (pDigestResponse)
  4112. {
  4113. DigestFreeMemory(pDigestResponse);
  4114. }
  4115. DebugLog((DEB_TRACE_FUNC, "DigestDecodeResponse: Leaving 0x%x\n", Status));
  4116. return(Status);
  4117. }