Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

811 lines
21 KiB

  1. /*++
  2. Copyright (c) 1989-1997 Microsoft Corporation
  3. Module Name:
  4. nlmain.c
  5. Abstract:
  6. This file contains the initialization and dispatch routines
  7. for the LAN Manager portions of the MSV1_0 authentication package.
  8. Author:
  9. Jim Kelly 11-Apr-1991
  10. Revision History:
  11. 25-Apr-1991 (cliffv)
  12. Added interactive logon support for PDK.
  13. Chandana Surlu 21-Jul-1996
  14. Stolen from \\kernel\razzle3\src\security\msv1_0\nlmain.c
  15. Adam Barr 15-Dec-1997
  16. Modified from private\security\msv_sspi\nlmain.c
  17. --*/
  18. #include <rdrssp.h>
  19. #include <nturtl.h>
  20. #include <align.h>
  21. #define NLP_ALLOCATE
  22. #include "nlp.h"
  23. #undef NLP_ALLOCATE
  24. #include <md4.h>
  25. #include <md5.h>
  26. #include <hmac.h>
  27. VOID
  28. NlpPutString(
  29. IN PUNICODE_STRING OutString,
  30. IN PUNICODE_STRING InString,
  31. IN PUCHAR *Where
  32. )
  33. /*++
  34. Routine Description:
  35. This routine copies the InString string to the memory pointed to by
  36. the Where parameter, and fixes the OutString string to point to that
  37. new copy.
  38. Parameters:
  39. OutString - A pointer to a destination NT string
  40. InString - A pointer to an NT string to be copied
  41. Where - A pointer to space to put the actual string for the
  42. OutString. The pointer is adjusted to point to the first byte
  43. following the copied string.
  44. Return Values:
  45. None.
  46. --*/
  47. {
  48. ASSERT( OutString != NULL );
  49. ASSERT( InString != NULL );
  50. ASSERT( Where != NULL && *Where != NULL);
  51. ASSERT( *Where == ROUND_UP_POINTER( *Where, sizeof(WCHAR) ) );
  52. #ifdef notdef
  53. KdPrint(("NlpPutString: %ld %Z\n", InString->Length, InString ));
  54. KdPrint((" InString: %lx %lx OutString: %lx Where: %lx\n", InString,
  55. InString->Buffer, OutString, *Where ));
  56. #endif
  57. if ( InString->Length > 0 ) {
  58. OutString->Buffer = (PWCH) *Where;
  59. OutString->MaximumLength = (USHORT)(InString->Length + sizeof(WCHAR));
  60. RtlCopyUnicodeString( OutString, InString );
  61. *Where += InString->Length;
  62. // *((WCHAR *)(*Where)) = L'\0';
  63. *(*Where) = '\0';
  64. *(*Where + 1) = '\0';
  65. *Where += 2;
  66. } else {
  67. RtlInitUnicodeString(OutString, NULL);
  68. }
  69. #ifdef notdef
  70. KdPrint((" OutString: %ld %lx\n", OutString->Length, OutString->Buffer));
  71. #endif
  72. return;
  73. }
  74. NTSTATUS
  75. NlpMakePrimaryCredential(
  76. IN PUNICODE_STRING LogonDomainName,
  77. IN PUNICODE_STRING UserName,
  78. IN PUNICODE_STRING CleartextPassword,
  79. OUT PMSV1_0_PRIMARY_CREDENTIAL *CredentialBuffer,
  80. OUT PULONG CredentialSize,
  81. IN BOOLEAN OwfPasswordProvided
  82. )
  83. /*++
  84. Routine Description:
  85. This routine makes a primary credential for the given user nam and
  86. password.
  87. Arguments:
  88. LogonDomainName - Is a string representing the domain in which the user's
  89. account is defined.
  90. UserName - Is a string representing the user's account name. The
  91. name may be up to 255 characters long. The name is treated case
  92. insensitive.
  93. CleartextPassword - Is a string containing the user's cleartext password.
  94. The password may be up to 255 characters long and contain any
  95. UNICODE value.
  96. CredentialBuffer - Returns a pointer to the specified credential allocated
  97. on the LsaHeap. It is the callers responsibility to deallocate
  98. this credential.
  99. CredentialSize - the size of the allocated credential buffer (in bytes).
  100. OwfPasswordProvided - If TRUE, then we assume the password is provided as the LM and NT OWF,
  101. passwords concatenated together.
  102. Return Value:
  103. STATUS_SUCCESS - Indicates the service completed successfully.
  104. STATUS_QUOTA_EXCEEDED - This error indicates that the logon
  105. could not be completed because the client does not have
  106. sufficient quota to allocate the return buffer.
  107. --*/
  108. {
  109. PMSV1_0_PRIMARY_CREDENTIAL Credential;
  110. NTSTATUS Status;
  111. PUCHAR Where;
  112. CHAR LmPassword[LM20_PWLEN+1];
  113. BOOLEAN LmPasswordPresent;
  114. STRING AnsiCleartextPassword;
  115. if (!OwfPasswordProvided) {
  116. //
  117. // Compute the Ansi version to the Cleartext password.
  118. //
  119. // The Ansi version of the Cleartext password is at most 14 bytes long,
  120. // exists in a trailing zero filled 15 byte buffer,
  121. // is uppercased.
  122. //
  123. AnsiCleartextPassword.Buffer = LmPassword;
  124. AnsiCleartextPassword.MaximumLength = sizeof(LmPassword);
  125. RtlZeroMemory( &LmPassword, sizeof(LmPassword) );
  126. Status = RtlUpcaseUnicodeStringToOemString(
  127. &AnsiCleartextPassword,
  128. CleartextPassword,
  129. (BOOLEAN) FALSE );
  130. if ( !NT_SUCCESS(Status) ) {
  131. RtlSecureZeroMemory( &LmPassword, sizeof(LmPassword) );
  132. AnsiCleartextPassword.Length = 0;
  133. LmPasswordPresent = FALSE;
  134. } else {
  135. LmPasswordPresent = TRUE;
  136. }
  137. }
  138. //
  139. // Build the credential
  140. //
  141. *CredentialSize = sizeof(MSV1_0_PRIMARY_CREDENTIAL) +
  142. LogonDomainName->Length + sizeof(WCHAR) +
  143. UserName->Length + sizeof(WCHAR);
  144. Credential = ExAllocatePool ( NonPagedPool, *CredentialSize );
  145. if ( Credential == NULL ) {
  146. KdPrint(("MSV1_0: NlpMakePrimaryCredential: No memory %ld\n",
  147. *CredentialSize ));
  148. return STATUS_QUOTA_EXCEEDED;
  149. }
  150. //
  151. // Put the LogonDomainName into the Credential Buffer.
  152. //
  153. Where = (PUCHAR)(Credential + 1);
  154. NlpPutString( &Credential->LogonDomainName, LogonDomainName, &Where );
  155. //
  156. // Put the UserName into the Credential Buffer.
  157. //
  158. NlpPutString( &Credential->UserName, UserName, &Where );
  159. if (OwfPasswordProvided) {
  160. RtlCopyMemory(&Credential->LmOwfPassword, CleartextPassword->Buffer, LM_OWF_PASSWORD_SIZE);
  161. Credential->LmPasswordPresent = TRUE;
  162. RtlCopyMemory(&Credential->NtOwfPassword, ((PUCHAR)CleartextPassword->Buffer) + LM_OWF_PASSWORD_SIZE, NT_OWF_PASSWORD_SIZE);
  163. Credential->NtPasswordPresent = TRUE;
  164. } else {
  165. //
  166. // Save the OWF encrypted versions of the passwords.
  167. //
  168. Status = RtlCalculateLmOwfPassword( LmPassword,
  169. &Credential->LmOwfPassword );
  170. ASSERT( NT_SUCCESS(Status) );
  171. Credential->LmPasswordPresent = LmPasswordPresent;
  172. Status = RtlCalculateNtOwfPassword( CleartextPassword,
  173. &Credential->NtOwfPassword );
  174. ASSERT( NT_SUCCESS(Status) );
  175. Credential->NtPasswordPresent = ( CleartextPassword->Length != 0 );
  176. //
  177. // Don't leave passwords around in the pagefile
  178. //
  179. RtlSecureZeroMemory( &LmPassword, sizeof(LmPassword) );
  180. }
  181. //
  182. // Return the credential to the caller.
  183. //
  184. *CredentialBuffer = Credential;
  185. return STATUS_SUCCESS;
  186. }
  187. VOID
  188. SspUpcaseUnicodeString(
  189. IN OUT UNICODE_STRING* pUnicodeString
  190. )
  191. /*++
  192. Routine Description:
  193. Upcase unicode string, modifying string in place.
  194. Arguments:
  195. pUnicodeString - string
  196. Return Value:
  197. none
  198. --*/
  199. {
  200. ULONG i;
  201. for (i = 0; i < pUnicodeString->Length / sizeof(WCHAR); i++)
  202. {
  203. pUnicodeString->Buffer[i] = RtlUpcaseUnicodeChar(pUnicodeString->Buffer[i]);
  204. }
  205. }
  206. #define MSV1_0_NTLMV2_OWF_LENGTH MSV1_0_NTLM3_RESPONSE_LENGTH
  207. VOID
  208. SspCalculateNtlmv2Owf(
  209. IN NT_OWF_PASSWORD* pNtOwfPassword,
  210. IN UNICODE_STRING* pUserName,
  211. IN UNICODE_STRING* pLogonDomainName,
  212. OUT UCHAR Ntlmv2Owf[MSV1_0_NTLMV2_OWF_LENGTH]
  213. )
  214. /*++
  215. Routine Description:
  216. Calculate Ntlm v2 OWF, salted with username and logon domain name
  217. Arguments:
  218. pNtOwfPassword - NT OWF
  219. pUserName - user name
  220. pLogonDomainName - logon domain name
  221. Ntlmv2Owf - NTLM v2 OWF
  222. Return Value:
  223. none
  224. --*/
  225. {
  226. HMACMD5_CTX HMACMD5Context;
  227. //
  228. // reserve a scratch buffer
  229. //
  230. WCHAR szUserName[(UNLEN + 4)] = {0};
  231. UNICODE_STRING UserName = {0, sizeof(szUserName), szUserName};
  232. //
  233. // first make a copy then upcase it
  234. //
  235. UserName.Length = min(UserName.MaximumLength, pUserName->Length);
  236. ASSERT(UserName.Length == pUserName->Length);
  237. memcpy(UserName.Buffer, pUserName->Buffer, UserName.Length);
  238. SspUpcaseUnicodeString(&UserName);
  239. //
  240. // Calculate Ntlmv2 OWF -- HMAC(MD4(P), (UserName, LogonDomainName))
  241. //
  242. HMACMD5Init(
  243. &HMACMD5Context,
  244. (UCHAR *) pNtOwfPassword,
  245. sizeof(*pNtOwfPassword)
  246. );
  247. HMACMD5Update(
  248. &HMACMD5Context,
  249. (UCHAR *) UserName.Buffer,
  250. UserName.Length
  251. );
  252. HMACMD5Update(
  253. &HMACMD5Context,
  254. (UCHAR *) pLogonDomainName->Buffer,
  255. pLogonDomainName->Length
  256. );
  257. HMACMD5Final(
  258. &HMACMD5Context,
  259. Ntlmv2Owf
  260. );
  261. }
  262. #define MSV1_0_NTLMV2_RESPONSE_LENGTH MSV1_0_NTLM3_RESPONSE_LENGTH
  263. VOID
  264. SspGetLmv2Response(
  265. IN NT_OWF_PASSWORD* pNtOwfPassword,
  266. IN UNICODE_STRING* pUserName,
  267. IN UNICODE_STRING* pLogonDomainName,
  268. IN UCHAR ChallengeToClient[MSV1_0_CHALLENGE_LENGTH],
  269. IN UCHAR ChallengeFromClient[MSV1_0_CHALLENGE_LENGTH],
  270. OUT UCHAR Response[MSV1_0_NTLMV2_RESPONSE_LENGTH],
  271. OUT USER_SESSION_KEY* pUserSessionKey,
  272. OUT UCHAR LanmanSessionKey[MSV1_0_LANMAN_SESSION_KEY_LENGTH]
  273. )
  274. /*++
  275. Routine Description:
  276. Get LMv2 response
  277. Arguments:
  278. pNtOwfPassword - NT OWF
  279. pUserName - user name
  280. pLogonDomainName - logon domain name
  281. ChallengeToClient - challenge to client
  282. pLmv2Response - Lm v2 response
  283. Routine - response
  284. Return Value:
  285. NTSTATUS
  286. --*/
  287. {
  288. HMACMD5_CTX HMACMD5Context;
  289. UCHAR Ntlmv2Owf[MSV1_0_NTLMV2_OWF_LENGTH];
  290. C_ASSERT(MD5DIGESTLEN == MSV1_0_NTLMV2_RESPONSE_LENGTH);
  291. //
  292. // get Ntlmv2 OWF
  293. //
  294. SspCalculateNtlmv2Owf(
  295. pNtOwfPassword,
  296. pUserName,
  297. pLogonDomainName,
  298. Ntlmv2Owf
  299. );
  300. //
  301. // Calculate Ntlmv2 Response
  302. // HMAC(Ntlmv2Owf, (ChallengeToClient, ChallengeFromClient))
  303. //
  304. HMACMD5Init(
  305. &HMACMD5Context,
  306. Ntlmv2Owf,
  307. MSV1_0_NTLMV2_OWF_LENGTH
  308. );
  309. HMACMD5Update(
  310. &HMACMD5Context,
  311. ChallengeToClient,
  312. MSV1_0_CHALLENGE_LENGTH
  313. );
  314. HMACMD5Update(
  315. &HMACMD5Context,
  316. ChallengeFromClient,
  317. MSV1_0_CHALLENGE_LENGTH
  318. );
  319. HMACMD5Final(
  320. &HMACMD5Context,
  321. Response
  322. );
  323. // now compute the session keys
  324. // HMAC(Kr, R)
  325. HMACMD5Init(
  326. &HMACMD5Context,
  327. Ntlmv2Owf,
  328. MSV1_0_NTLM3_OWF_LENGTH
  329. );
  330. HMACMD5Update(
  331. &HMACMD5Context,
  332. Response,
  333. MSV1_0_NTLM3_RESPONSE_LENGTH
  334. );
  335. ASSERT(MD5DIGESTLEN == MSV1_0_USER_SESSION_KEY_LENGTH);
  336. HMACMD5Final(
  337. &HMACMD5Context,
  338. (PUCHAR)pUserSessionKey
  339. );
  340. ASSERT(MSV1_0_LANMAN_SESSION_KEY_LENGTH <= MSV1_0_USER_SESSION_KEY_LENGTH);
  341. RtlCopyMemory(
  342. LanmanSessionKey,
  343. pUserSessionKey,
  344. MSV1_0_LANMAN_SESSION_KEY_LENGTH
  345. );
  346. }
  347. typedef struct {
  348. UCHAR Response[MSV1_0_NTLM3_RESPONSE_LENGTH];
  349. UCHAR ChallengeFromClient[MSV1_0_CHALLENGE_LENGTH];
  350. } MSV1_0_LM3_RESPONSE, *PMSV1_0_LM3_RESPONSE;
  351. #define NULL_SESSION_REQUESTED 0x10
  352. NTSTATUS
  353. MspLm20GetChallengeResponse (
  354. IN PVOID ProtocolSubmitBuffer,
  355. IN ULONG SubmitBufferSize,
  356. OUT PVOID *ProtocolReturnBuffer,
  357. OUT PULONG ReturnBufferSize,
  358. IN BOOLEAN OwfPasswordProvided
  359. )
  360. /*++
  361. Routine Description:
  362. This routine is the dispatch routine for LsaCallAuthenticationPackage()
  363. with a message type of MsV1_0Lm20GetChallengeResponse. It is called by
  364. the LanMan redirector to determine the Challenge Response to pass to a
  365. server when trying to establish a connection to the server.
  366. This routine is passed a Challenge from the server. This routine encrypts
  367. the challenge with either the specified password or with the password
  368. implied by the specified Logon Id.
  369. Two Challenge responses are returned. One is based on the Unicode password
  370. as given to the Authentication package. The other is based on that
  371. password converted to a multi-byte character set (e.g., ASCII) and upper
  372. cased. The redirector should use whichever (or both) challenge responses
  373. as it needs them.
  374. Arguments:
  375. The first four arguments to this routine are identical to those of LsaApCallPackage.
  376. Only the special attributes of these parameters as they apply to
  377. this routine are mentioned here.
  378. OwfPasswordProvided use is used to distinquish if the password is Owf or not.
  379. Return Value:
  380. STATUS_SUCCESS - Indicates the service completed successfully.
  381. STATUS_QUOTA_EXCEEDED - This error indicates that the logon
  382. could not be completed because the client does not have
  383. sufficient quota to allocate the return buffer.
  384. --*/
  385. {
  386. NTSTATUS Status = STATUS_SUCCESS;
  387. PMSV1_0_GETCHALLENRESP_REQUEST GetRespRequest;
  388. PMSV1_0_GETCHALLENRESP_RESPONSE GetRespResponse;
  389. PMSV1_0_PRIMARY_CREDENTIAL Credential = NULL;
  390. PMSV1_0_PRIMARY_CREDENTIAL BuiltCredential = NULL;
  391. PVOID ClientBuffer = NULL;
  392. PUCHAR ClientStrings;
  393. //
  394. // Responses to return to the caller.
  395. //
  396. MSV1_0_LM3_RESPONSE LmResp = {0};
  397. STRING LmResponseString;
  398. NT_RESPONSE NtResponse = {0};
  399. STRING NtResponseString;
  400. UNICODE_STRING NullUnicodeString = {0};
  401. USER_SESSION_KEY UserSessionKey;
  402. UCHAR LanmanSessionKey[MSV1_0_LANMAN_SESSION_KEY_LENGTH];
  403. ULONG CredentialSize = 0;
  404. RtlZeroMemory( &UserSessionKey, sizeof(UserSessionKey) );
  405. RtlZeroMemory( LanmanSessionKey, sizeof(LanmanSessionKey) );
  406. //
  407. // If no credentials are associated with the client, a null session
  408. // will be used. For a downlevel server, the null session response is
  409. // a 1-byte null string (\0). Initialize LmResponseString to the
  410. // null session response.
  411. //
  412. RtlInitString( &LmResponseString, "" );
  413. LmResponseString.Length = 1;
  414. //
  415. // Initialize the NT response to the NT null session credentials,
  416. // which are zero length.
  417. //
  418. RtlInitString( &NtResponseString, NULL );
  419. //
  420. // Ensure the specified Submit Buffer is of reasonable size and
  421. // relocate all of the pointers to be relative to the LSA allocated
  422. // buffer.
  423. //
  424. if ( SubmitBufferSize < sizeof(MSV1_0_GETCHALLENRESP_REQUEST) ) {
  425. Status = STATUS_INVALID_PARAMETER;
  426. goto Cleanup;
  427. }
  428. GetRespRequest = (PMSV1_0_GETCHALLENRESP_REQUEST) ProtocolSubmitBuffer;
  429. ASSERT( GetRespRequest->MessageType == MsV1_0Lm20GetChallengeResponse );
  430. //
  431. // If the caller wants information from the credentials of a specified
  432. // LogonId, get those credentials from the LSA.
  433. //
  434. // If there are no such credentials,
  435. // tell the caller to use the NULL session.
  436. //
  437. #define PRIMARY_CREDENTIAL_NEEDED \
  438. (RETURN_PRIMARY_USERNAME | \
  439. USE_PRIMARY_PASSWORD )
  440. if ( ((GetRespRequest->ParameterControl & PRIMARY_CREDENTIAL_NEEDED) != 0 ) && ((GetRespRequest->ParameterControl & NULL_SESSION_REQUESTED) == 0)) {
  441. ASSERT(FALSE);
  442. }
  443. //
  444. // If the caller passed in a password to use,
  445. // use it to build a credential.
  446. //
  447. // The password is assumed to be the LM and NT OWF
  448. // passwords concatenated together.
  449. //
  450. if ( (GetRespRequest->ParameterControl & USE_PRIMARY_PASSWORD) == 0 ) {
  451. Status = NlpMakePrimaryCredential( &GetRespRequest->LogonDomainName,
  452. &GetRespRequest->UserName,
  453. &GetRespRequest->Password,
  454. &BuiltCredential,
  455. &CredentialSize,
  456. OwfPasswordProvided
  457. );
  458. if ( !NT_SUCCESS( Status ) ) {
  459. goto Cleanup;
  460. }
  461. //
  462. // Use the newly allocated credential to get the password information
  463. // from.
  464. //
  465. Credential = BuiltCredential;
  466. }
  467. //
  468. // Build the appropriate response.
  469. //
  470. if ( Credential != NULL ) {
  471. ASSERT(Credential->UserName.Length);
  472. SspGetLmv2Response(
  473. &Credential->NtOwfPassword,
  474. &Credential->UserName,
  475. &Credential->LogonDomainName,
  476. GetRespRequest->ChallengeToClient,
  477. LmResp.ChallengeFromClient,
  478. LmResp.Response,
  479. &UserSessionKey,
  480. LanmanSessionKey
  481. );
  482. LmResponseString.Buffer = (UCHAR*) &LmResp;
  483. LmResponseString.Length = LmResponseString.MaximumLength = sizeof(LmResp);
  484. NtResponseString.Buffer = (CHAR*) L"";
  485. NtResponseString.Length = 0;
  486. NtResponseString.MaximumLength = sizeof(WCHAR);
  487. //
  488. // Compute the session keys
  489. //
  490. if ( GetRespRequest->ParameterControl & RETURN_NON_NT_USER_SESSION_KEY) {
  491. //
  492. // If the redir didn't negotiate an NT protocol with the server,
  493. // use the lanman session key.
  494. //
  495. if ( Credential->LmPasswordPresent ) {
  496. ASSERT( sizeof(UserSessionKey) >= sizeof(LanmanSessionKey) );
  497. RtlCopyMemory( &UserSessionKey,
  498. &Credential->LmOwfPassword,
  499. sizeof(LanmanSessionKey) );
  500. }
  501. if ( Credential->LmPasswordPresent ) {
  502. RtlCopyMemory( LanmanSessionKey,
  503. &Credential->LmOwfPassword,
  504. sizeof(LanmanSessionKey) );
  505. }
  506. } else {
  507. if ( !Credential->NtPasswordPresent ) {
  508. RtlCopyMemory( &Credential->NtOwfPassword,
  509. &NlpNullNtOwfPassword,
  510. sizeof(Credential->NtOwfPassword) );
  511. }
  512. }
  513. }
  514. //
  515. // Allocate a buffer to return to the caller.
  516. //
  517. *ReturnBufferSize = sizeof(MSV1_0_GETCHALLENRESP_RESPONSE) +
  518. Credential->LogonDomainName.Length + sizeof(WCHAR) +
  519. Credential->UserName.Length + sizeof(WCHAR) +
  520. NtResponseString.Length + sizeof(WCHAR) +
  521. LmResponseString.Length + sizeof(WCHAR);
  522. ClientBuffer = ExAllocatePool(NonPagedPool, *ReturnBufferSize);
  523. if (ClientBuffer == NULL) {
  524. Status = STATUS_INSUFFICIENT_RESOURCES;
  525. goto Cleanup;
  526. }
  527. GetRespResponse = (PMSV1_0_GETCHALLENRESP_RESPONSE) ClientBuffer;
  528. //
  529. // Fill in the return buffer.
  530. //
  531. GetRespResponse->MessageType = MsV1_0Lm20GetChallengeResponse;
  532. RtlCopyMemory( GetRespResponse->UserSessionKey,
  533. &UserSessionKey,
  534. sizeof(UserSessionKey));
  535. RtlCopyMemory( GetRespResponse->LanmanSessionKey,
  536. LanmanSessionKey,
  537. sizeof(LanmanSessionKey) );
  538. ClientStrings = ((PUCHAR)ClientBuffer) + sizeof(MSV1_0_GETCHALLENRESP_RESPONSE);
  539. //
  540. // Copy the logon domain name (the string may be empty)
  541. //
  542. NlpPutString(
  543. &GetRespResponse->LogonDomainName,
  544. &Credential->LogonDomainName,
  545. &ClientStrings );
  546. //
  547. // Copy the user name (the string may be empty)
  548. //
  549. NlpPutString(
  550. &GetRespResponse->UserName,
  551. &Credential->UserName,
  552. &ClientStrings );
  553. //
  554. // Copy the Challenge Responses to the client buffer.
  555. //
  556. NlpPutString(
  557. (PUNICODE_STRING)
  558. &GetRespResponse->CaseSensitiveChallengeResponse,
  559. (PUNICODE_STRING) &NtResponseString,
  560. &ClientStrings );
  561. NlpPutString(
  562. (PUNICODE_STRING)
  563. &GetRespResponse->CaseInsensitiveChallengeResponse,
  564. (PUNICODE_STRING)&LmResponseString,
  565. &ClientStrings );
  566. *ProtocolReturnBuffer = ClientBuffer;
  567. Cleanup:
  568. //
  569. // If we weren't successful, free the buffer in the clients address space.
  570. //
  571. if ( !NT_SUCCESS(Status) && ( ClientBuffer != NULL ) ) {
  572. ExFreePool(ClientBuffer);
  573. }
  574. //
  575. // Cleanup locally used resources
  576. //
  577. if ( BuiltCredential != NULL ) {
  578. RtlZeroMemory(BuiltCredential, CredentialSize);
  579. ExFreePool(BuiltCredential);
  580. }
  581. //
  582. // Return status to the caller.
  583. //
  584. return Status;
  585. }