Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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